<commit_before>package bypasser

import (
	//"bytes"
	"encoding/binary"
	"errors"
	//"fmt"
	//"io"
	"crypto/aes"
	"crypto/cipher"
	"net"
	"strconv"
	//"time"
)

const (
	reserved = 0x00
)

const (
	bufferSize       = aes.BlockSize
	minRequestLength = 10
)

const (
	commandConnect      = 0x01
	commandBind         = 0x02
	commandUDPAssociate = 0x03
)

const (
	addressTypeIPv4       = 0x01
	addressTypeDomainName = 0x03
	addressTypeIPv6       = 0x04
)

const (
	replySucceeded                 = 0x00
	replyGeneralSOCKSServerFailure = 0x01
	replyConnectionNotAllowed      = 0x02
	replyNetworkUnreachable        = 0x03
	replyHostUnreachable           = 0x04
	replyConnectionRefused         = 0x05
	replyTTLExpired                = 0x06
	replyCommandNotSupported       = 0x07
	replyAddressTypeNotSupported   = 0x08
)

type request struct {
	version            byte
	command            byte
	rsv                byte
	addressType        byte
	destinationAddress []byte
	destinationPort    [2]byte
}

type response struct {
	version      byte
	reply        byte
	rsv          byte
	addressType  byte
	boundAddress []byte
	boundPort    [2]byte
}

func HandleRequest(rqst []byte, conn *net.TCPConn) ([]byte, error) {
	req, ok := formatRequest(rqst)
	if !ok {
		return []byte{}, errors.New("Invalid request packet.")
	}
	switch req.command {
	case commandConnect:
		{
			log("Handling command connect.", 3)
			return parseResponse(handleConnect(req, conn)), nil
		}
	default:
		{
			log("Command not supported.", 3)
			return parseResponse(generateFailureResponse(req.version, replyCommandNotSupported)), nil
		}
	}
}

func handleConnect(req request, conn *net.TCPConn) response {
	switch req.addressType {
	case addressTypeIPv4:
		{
			log("Address type is IPv4, start connecting.", 4)
			return startTcpConnectSession(req.version, req.destinationAddress, addressTypeIPv4, req.destinationPort, conn)
		}
	case addressTypeDomainName:
		{
			log("Address type is Domain name, start connecting.", 4)
			//log("Requested domain name is: "+string(req.destinationAddress[1:]), 4)
			//return generateFailureResponse(req.version, replyAddressTypeNotSupported)
			return startTcpConnectSession(req.version, req.destinationAddress, addressTypeDomainName, req.destinationPort, conn)
		}
	default:
		{
			log("Address type is not supported: "+strconv.Itoa(int(req.addressType)), 4)
			return generateFailureResponse(req.version, replyAddressTypeNotSupported)
		}
	}
}

func generateFailureResponse(version byte, reply byte) response {
	return response{version, reply, reserved, 0, []byte{}, [2]byte{0, 0}}
}

func startTcpConnectSession(version byte, addr []byte, addrType byte, port [2]byte, conn *net.TCPConn) response {
	var addrString string
	if addrType == addressTypeDomainName {
		addrString = string(addr[1:])
	} else {
		addrString = net.IP(addr).String()
	}
	log("Start to resolve the address: "+addrString+":"+formatPort(port), 5)
	targetAddr, err := net.ResolveTCPAddr("tcp", addrString+":"+formatPort(port))
	if err != nil {
		log("Fail to resolve tcp address.", 5)
		resp := generateFailureResponse(version, replyGeneralSOCKSServerFailure)
		return resp
	}
	connToTarget, err := net.DialTCP("tcp", nil, targetAddr)
	if err != nil {
		log("Failed to connect to the target.", 5)
		resp := generateFailureResponse(version, replyHostUnreachable)
		return resp
	} else {
		log("Connected to target.", 5)
		ipAddr, portNumber, _ := net.SplitHostPort(connToTarget.LocalAddr().String())
		ipAddrBytes := net.ParseIP(ipAddr)
		var addrType byte
		if ipAddrBytes.To4() != nil {
			log("Bound ip address is an IPv4 address", 5)
			ipAddrBytes = ipAddrBytes.To4()
			addrType = addressTypeIPv4
		} else {
			log("Bound ip address is an IPv6 address", 5)
			addrType = addressTypeIPv6
		}
		log("Bound port is: "+portNumber, 5)
		go buildTunnel(connToTarget, conn)
		return response{version, replySucceeded, reserved, addrType, ipAddrBytes, parsePort(portNumber)}
		// localAddr, _ := net.ResolveTCPAddr("tcp", ":0")
		// ln, err := net.ListenTCP("tcp", localAddr)
		// if err != nil {
		// 	log("Failed to get the listener to listen client.", 5)
		// 	resp := generateFailureResponse(version, replyGeneralSOCKSServerFailure)
		// 	return resp
		// } else {
		// 	log("Listener created.", 5)
		// 	networkAddr := ln.Addr().String()
		// 	//networkAddr := connToTarget.LocalAddr().String()
		// 	log("Local address is: "+networkAddr+".", 5)
		// 	ipAddr, portNumber, splitError := net.SplitHostPort(networkAddr)
		// 	if splitError != nil {
		// 		log("Failed to split the listen's local address.", 5)
		// 		resp := generateFailureResponse(version, replyGeneralSOCKSServerFailure)
		// 		return resp
		// 	} else {
		// 		ipAddr = "127.0.0.1"
		// ipAddrBytes := net.ParseIP(ipAddr)
		// var addrType byte
		// if ipAddrBytes.To4() != nil {
		// 	log("Bound ip address is an IPv4 address", 5)
		// 	addrType = addressTypeIPv4
		// } else {
		// 	log("Bound ip address is an IPv6 address", 5)
		// 	addrType = addressTypeIPv6
		// }
		// log("Bound port is: "+portNumber, 5)
		// 		resp := response{version, replySucceeded, reserved, addrType, ipAddrBytes, parsePort(portNumber)}

		// 		go waitClientToConnect(ln, connToTarget)
		// 		return resp

		// 	}
		// }
	}
}

// func waitClientToConnect(ln *net.TCPListener, connToTarget *net.TCPConn) {
// 	log("Waiting client to connect.", 5)
// 	connToClient, err := ln.AcceptTCP()
// 	log("Client connected.", 6)
// 	if err != nil {
// 		log("Client connected, but error occurs: "+err.Error(), 6)
// 		//break
// 	} else {
// 		log("Start building tunnel.", 6)
// 		go BuildTunnel(connToTarget, connToClient)
// 	}
// }

func buildTunnel(fromTarget, toClient *net.TCPConn) {
	// defer fromTarget.Close()

	// if _, err := fromTarget.Write(buf.Bytes()); err != nil {
	// 	panic(err)
	// }

	// data := make([]byte, 1024)
	// n, err := fromTarget.Read(data)
	// if err != nil {
	// 	if err != io.EOF {
	// 		panic(err)
	// 	} else {
	// 		toClient.Write(data[:n])

	// 	}
	// }
	// toClient.Close()
	tunnelForward := make(chan []byte)
	tunnelBackward := make(chan []byte)
	errorChannelForward := make(chan error)
	errorChannelBackward := make(chan error)
	go handleTunnel(fromTarget, tunnelForward, tunnelBackward, errorChannelForward, errorChannelBackward, true)
	go handleTunnel(toClient, tunnelBackward, tunnelForward, errorChannelBackward, errorChannelForward, false)

	// go func() {
	// 	for {
	// 		io.Copy(fromTarget, toClient)
	// 	}
	// }()
	// go func() {
	// 	for {
	// 		io.Copy(toClient, fromTarget)
	// 	}
	// }()
}

func handleTunnel(target *net.TCPConn, receiver <-chan []byte, sender chan<- []byte, errorChannelF <-chan error, errorChannelB chan<- error, shouldEncrypt bool) {
	errChan := make(chan error)
	dataChan := make(chan []byte)
	go func(dch chan []byte, ech chan error) {
		for {
			// buf := &bytes.Buffer{}
			// for {
			// 	data := make([]byte, 256)
			// 	n, err := target.Read(data)
			// 	if err != nil {
			// 		if err == io.EOF {
			// 			log("End of file encountered: "+err.Error(), 6)
			// 			break
			// 		} else {
			// 			log("Other error encountered: "+err.Error(), 6)
			// 			ech <- err
			// 			return
			// 		}

			// 	}
			// 	buf.Write(data[:n])
			// 	errChan <- errors.New("")
			// 	if data[n-2] == 13 && data[n-1] == 10 {
			// 		log("End of file encountered.", 6)
			// 		break
			// 	}
			// }
			// dch <- buf.Bytes()
			// return
			buf := make([]byte, bufferSize)
			const key16 = "1234567890123456"
			var key = key16
			var iv = []byte(key)[:aes.BlockSize]
			//target.SetReadDeadline(time.Now())
			length, err := target.Read(buf)
			if err != nil {
				log("Error encountered: "+err.Error(), 6)
				ech <- err
				return
			} else {
				//target.SetReadDeadline(time.Time{})
				log(strconv.FormatInt(int64(length), 10)+" bytes of data received, sent to data channel.", 6)
				result := make([]byte, length)
				if shouldEncrypt {

					EncryptAESCFB(result, buf[:length], []byte(key), iv)
				} else {
					DecryptAESCFB(result, buf[:length], []byte(key), iv)
				}
				dch <- result
			}

		}
	}(dataChan, errChan)

	for {
		select {
		case data := <-dataChan:
			//log("Data received: "+string(data), 6)
			log("Data received from data channel, pass it to sender.", 6)
			sender <- data
		case err := <-errChan:
			// handle our error then exit for loop
			log("Error received from error channel: "+err.Error()+", notify the other routine to stop.", 6)
			errorChannelB <- err
			log("Tunnel ended.", 6)
			target.Close()
			return
		case err := <-errorChannelF:
			log("Error received from the other routine: "+err.Error()+", closing the connection.", 6)
			log("Tunnel ended.", 6)
			target.Close()
			return
		case data := <-receiver:
			log("Data received from receiver.", 6)
			target.Write(data)
		}
	}

}

func parsePort(p string) [2]byte {
	portNumber, _ := strconv.ParseUint(p, 10, 16)
	var result [2]byte
	binary.BigEndian.PutUint16(result[0:2], uint16(portNumber))
	return result
}

func formatPort(p [2]byte) string {
	return strconv.FormatUint(uint64(binary.BigEndian.Uint16(p[0:2])), 10)
}

func formatRequest(req []byte) (request, bool) {
	reqLen := len(req)
	if reqLen < minRequestLength {
		return request{}, false
	} else {
		rqst, ok := request{req[0], req[1], req[2], req[3], req[4 : reqLen-2], [2]byte{req[reqLen-2], req[reqLen-1]}}, true
		if rqst.addressType == addressTypeDomainName {
			//log()
		}
		return rqst, ok
	}
}

func parseResponse(resp response) []byte {
	//return []byte{resp.version, resp.reply, resp.rsv, resp.addressType, 0, 0, 0, 0, 0, 0}
	return append(append([]byte{resp.version, resp.reply, resp.rsv, resp.addressType}, resp.boundAddress...), resp.boundPort[:]...)
}

func log(msg string, lvl int) {
	blank := ""
	for i := 0; i < lvl; i++ {
		blank += "   "
	}
	//fmt.Println("GoFWBypasser:", blank, msg)
}
func EncryptAESCFB(dst, src, key, iv []byte) error {
	aesBlockEncrypter, err := aes.NewCipher([]byte(key))
	if err != nil {
		return err
	}
	aesEncrypter := cipher.NewCFBEncrypter(aesBlockEncrypter, iv)
	aesEncrypter.XORKeyStream(dst, src)
	return nil
}

func DecryptAESCFB(dst, src, key, iv []byte) error {
	aesBlockDecrypter, err := aes.NewCipher([]byte(key))
	if err != nil {
		return nil
	}
	aesDecrypter := cipher.NewCFBDecrypter(aesBlockDecrypter, iv)
	aesDecrypter.XORKeyStream(dst, src)
	return nil
}
<commit_msg>testing<commit_after>package bypasser

import (
	//"bytes"
	"encoding/binary"
	"errors"
	//"fmt"
	//"io"
	"crypto/aes"
	"crypto/cipher"
	"net"
	"strconv"
	//"time"
)

const (
	reserved = 0x00
)

const (
	bufferSize       = 128
	minRequestLength = 10
)

const (
	commandConnect      = 0x01
	commandBind         = 0x02
	commandUDPAssociate = 0x03
)

const (
	addressTypeIPv4       = 0x01
	addressTypeDomainName = 0x03
	addressTypeIPv6       = 0x04
)

const (
	replySucceeded                 = 0x00
	replyGeneralSOCKSServerFailure = 0x01
	replyConnectionNotAllowed      = 0x02
	replyNetworkUnreachable        = 0x03
	replyHostUnreachable           = 0x04
	replyConnectionRefused         = 0x05
	replyTTLExpired                = 0x06
	replyCommandNotSupported       = 0x07
	replyAddressTypeNotSupported   = 0x08
)

type request struct {
	version            byte
	command            byte
	rsv                byte
	addressType        byte
	destinationAddress []byte
	destinationPort    [2]byte
}

type response struct {
	version      byte
	reply        byte
	rsv          byte
	addressType  byte
	boundAddress []byte
	boundPort    [2]byte
}

func HandleRequest(rqst []byte, conn *net.TCPConn) ([]byte, error) {
	req, ok := formatRequest(rqst)
	if !ok {
		return []byte{}, errors.New("Invalid request packet.")
	}
	switch req.command {
	case commandConnect:
		{
			log("Handling command connect.", 3)
			return parseResponse(handleConnect(req, conn)), nil
		}
	default:
		{
			log("Command not supported.", 3)
			return parseResponse(generateFailureResponse(req.version, replyCommandNotSupported)), nil
		}
	}
}

func handleConnect(req request, conn *net.TCPConn) response {
	switch req.addressType {
	case addressTypeIPv4:
		{
			log("Address type is IPv4, start connecting.", 4)
			return startTcpConnectSession(req.version, req.destinationAddress, addressTypeIPv4, req.destinationPort, conn)
		}
	case addressTypeDomainName:
		{
			log("Address type is Domain name, start connecting.", 4)
			//log("Requested domain name is: "+string(req.destinationAddress[1:]), 4)
			//return generateFailureResponse(req.version, replyAddressTypeNotSupported)
			return startTcpConnectSession(req.version, req.destinationAddress, addressTypeDomainName, req.destinationPort, conn)
		}
	default:
		{
			log("Address type is not supported: "+strconv.Itoa(int(req.addressType)), 4)
			return generateFailureResponse(req.version, replyAddressTypeNotSupported)
		}
	}
}

func generateFailureResponse(version byte, reply byte) response {
	return response{version, reply, reserved, 0, []byte{}, [2]byte{0, 0}}
}

func startTcpConnectSession(version byte, addr []byte, addrType byte, port [2]byte, conn *net.TCPConn) response {
	var addrString string
	if addrType == addressTypeDomainName {
		addrString = string(addr[1:])
	} else {
		addrString = net.IP(addr).String()
	}
	log("Start to resolve the address: "+addrString+":"+formatPort(port), 5)
	targetAddr, err := net.ResolveTCPAddr("tcp", addrString+":"+formatPort(port))
	if err != nil {
		log("Fail to resolve tcp address.", 5)
		resp := generateFailureResponse(version, replyGeneralSOCKSServerFailure)
		return resp
	}
	connToTarget, err := net.DialTCP("tcp", nil, targetAddr)
	if err != nil {
		log("Failed to connect to the target.", 5)
		resp := generateFailureResponse(version, replyHostUnreachable)
		return resp
	} else {
		log("Connected to target.", 5)
		ipAddr, portNumber, _ := net.SplitHostPort(connToTarget.LocalAddr().String())
		ipAddrBytes := net.ParseIP(ipAddr)
		var addrType byte
		if ipAddrBytes.To4() != nil {
			log("Bound ip address is an IPv4 address", 5)
			ipAddrBytes = ipAddrBytes.To4()
			addrType = addressTypeIPv4
		} else {
			log("Bound ip address is an IPv6 address", 5)
			addrType = addressTypeIPv6
		}
		log("Bound port is: "+portNumber, 5)
		go buildTunnel(connToTarget, conn)
		return response{version, replySucceeded, reserved, addrType, ipAddrBytes, parsePort(portNumber)}
		// localAddr, _ := net.ResolveTCPAddr("tcp", ":0")
		// ln, err := net.ListenTCP("tcp", localAddr)
		// if err != nil {
		// 	log("Failed to get the listener to listen client.", 5)
		// 	resp := generateFailureResponse(version, replyGeneralSOCKSServerFailure)
		// 	return resp
		// } else {
		// 	log("Listener created.", 5)
		// 	networkAddr := ln.Addr().String()
		// 	//networkAddr := connToTarget.LocalAddr().String()
		// 	log("Local address is: "+networkAddr+".", 5)
		// 	ipAddr, portNumber, splitError := net.SplitHostPort(networkAddr)
		// 	if splitError != nil {
		// 		log("Failed to split the listen's local address.", 5)
		// 		resp := generateFailureResponse(version, replyGeneralSOCKSServerFailure)
		// 		return resp
		// 	} else {
		// 		ipAddr = "127.0.0.1"
		// ipAddrBytes := net.ParseIP(ipAddr)
		// var addrType byte
		// if ipAddrBytes.To4() != nil {
		// 	log("Bound ip address is an IPv4 address", 5)
		// 	addrType = addressTypeIPv4
		// } else {
		// 	log("Bound ip address is an IPv6 address", 5)
		// 	addrType = addressTypeIPv6
		// }
		// log("Bound port is: "+portNumber, 5)
		// 		resp := response{version, replySucceeded, reserved, addrType, ipAddrBytes, parsePort(portNumber)}

		// 		go waitClientToConnect(ln, connToTarget)
		// 		return resp

		// 	}
		// }
	}
}

// func waitClientToConnect(ln *net.TCPListener, connToTarget *net.TCPConn) {
// 	log("Waiting client to connect.", 5)
// 	connToClient, err := ln.AcceptTCP()
// 	log("Client connected.", 6)
// 	if err != nil {
// 		log("Client connected, but error occurs: "+err.Error(), 6)
// 		//break
// 	} else {
// 		log("Start building tunnel.", 6)
// 		go BuildTunnel(connToTarget, connToClient)
// 	}
// }

func buildTunnel(fromTarget, toClient *net.TCPConn) {
	// defer fromTarget.Close()

	// if _, err := fromTarget.Write(buf.Bytes()); err != nil {
	// 	panic(err)
	// }

	// data := make([]byte, 1024)
	// n, err := fromTarget.Read(data)
	// if err != nil {
	// 	if err != io.EOF {
	// 		panic(err)
	// 	} else {
	// 		toClient.Write(data[:n])

	// 	}
	// }
	// toClient.Close()
	tunnelForward := make(chan []byte)
	tunnelBackward := make(chan []byte)
	errorChannelForward := make(chan error)
	errorChannelBackward := make(chan error)
	go handleTunnel(fromTarget, tunnelForward, tunnelBackward, errorChannelForward, errorChannelBackward, true)
	go handleTunnel(toClient, tunnelBackward, tunnelForward, errorChannelBackward, errorChannelForward, false)

	// go func() {
	// 	for {
	// 		io.Copy(fromTarget, toClient)
	// 	}
	// }()
	// go func() {
	// 	for {
	// 		io.Copy(toClient, fromTarget)
	// 	}
	// }()
}

func handleTunnel(target *net.TCPConn, receiver <-chan []byte, sender chan<- []byte, errorChannelF <-chan error, errorChannelB chan<- error, shouldEncrypt bool) {
	errChan := make(chan error)
	dataChan := make(chan []byte)
	go func(dch chan []byte, ech chan error) {
		for {
			// buf := &bytes.Buffer{}
			// for {
			// 	data := make([]byte, 256)
			// 	n, err := target.Read(data)
			// 	if err != nil {
			// 		if err == io.EOF {
			// 			log("End of file encountered: "+err.Error(), 6)
			// 			break
			// 		} else {
			// 			log("Other error encountered: "+err.Error(), 6)
			// 			ech <- err
			// 			return
			// 		}

			// 	}
			// 	buf.Write(data[:n])
			// 	errChan <- errors.New("")
			// 	if data[n-2] == 13 && data[n-1] == 10 {
			// 		log("End of file encountered.", 6)
			// 		break
			// 	}
			// }
			// dch <- buf.Bytes()
			// return
			buf := make([]byte, bufferSize)
			const key16 = "1234567890123456"
			var key = key16
			var iv = []byte(key)[:aes.BlockSize]
			//target.SetReadDeadline(time.Now())
			length, err := target.Read(buf)
			if err != nil {
				log("Error encountered: "+err.Error(), 6)
				ech <- err
				return
			} else {
				//target.SetReadDeadline(time.Time{})
				log(strconv.FormatInt(int64(length), 10)+" bytes of data received, sent to data channel.", 6)
				result := make([]byte, length)
				if shouldEncrypt {

					EncryptAESCFB(result, buf[:length], []byte(key), iv)
				} else {
					DecryptAESCFB(result, buf[:length], []byte(key), iv)
				}
				dch <- result
			}

		}
	}(dataChan, errChan)

	for {
		select {
		case data := <-dataChan:
			//log("Data received: "+string(data), 6)
			log("Data received from data channel, pass it to sender.", 6)
			sender <- data
		case err := <-errChan:
			// handle our error then exit for loop
			log("Error received from error channel: "+err.Error()+", notify the other routine to stop.", 6)
			errorChannelB <- err
			log("Tunnel ended.", 6)
			target.Close()
			return
		case err := <-errorChannelF:
			log("Error received from the other routine: "+err.Error()+", closing the connection.", 6)
			log("Tunnel ended.", 6)
			target.Close()
			return
		case data := <-receiver:
			log("Data received from receiver.", 6)
			target.Write(data)
		}
	}

}

func parsePort(p string) [2]byte {
	portNumber, _ := strconv.ParseUint(p, 10, 16)
	var result [2]byte
	binary.BigEndian.PutUint16(result[0:2], uint16(portNumber))
	return result
}

func formatPort(p [2]byte) string {
	return strconv.FormatUint(uint64(binary.BigEndian.Uint16(p[0:2])), 10)
}

func formatRequest(req []byte) (request, bool) {
	reqLen := len(req)
	if reqLen < minRequestLength {
		return request{}, false
	} else {
		rqst, ok := request{req[0], req[1], req[2], req[3], req[4 : reqLen-2], [2]byte{req[reqLen-2], req[reqLen-1]}}, true
		if rqst.addressType == addressTypeDomainName {
			//log()
		}
		return rqst, ok
	}
}

func parseResponse(resp response) []byte {
	//return []byte{resp.version, resp.reply, resp.rsv, resp.addressType, 0, 0, 0, 0, 0, 0}
	return append(append([]byte{resp.version, resp.reply, resp.rsv, resp.addressType}, resp.boundAddress...), resp.boundPort[:]...)
}

func log(msg string, lvl int) {
	blank := ""
	for i := 0; i < lvl; i++ {
		blank += "   "
	}
	//fmt.Println("GoFWBypasser:", blank, msg)
}
func EncryptAESCFB(dst, src, key, iv []byte) error {
	aesBlockEncrypter, err := aes.NewCipher([]byte(key))
	if err != nil {
		return err
	}
	aesEncrypter := cipher.NewCFBEncrypter(aesBlockEncrypter, iv)
	aesEncrypter.XORKeyStream(dst, src)
	return nil
}

func DecryptAESCFB(dst, src, key, iv []byte) error {
	aesBlockDecrypter, err := aes.NewCipher([]byte(key))
	if err != nil {
		return nil
	}
	aesDecrypter := cipher.NewCFBDecrypter(aesBlockDecrypter, iv)
	aesDecrypter.XORKeyStream(dst, src)
	return nil
}
<|endoftext|>


<commit_before>package argon2

import (
	"crypto/subtle"
	"encoding/base64"
	"errors"
	"fmt"
	"strings"

	"golang.org/x/crypto/argon2"
)

// Errors returned by Argon2Hasher.
var (
	ErrHashComponentUnreadable = errors.New("unchained/argon2: unreadable component in hashed password")
	ErrHashComponentMismatch   = errors.New("unchained/argon2: hashed password components mismatch")
	ErrAlgorithmMismatch       = errors.New("unchained/argon2: algorithm mismatch")
	ErrIncompatibleVersion     = errors.New("unchained/argon2: incompatible version")
)

// Argon2Hasher implements Argon2i password hasher.
type Argon2Hasher struct {
	// Algorithm identifier.
	Algorithm string
	// Defines the amount of computation time, given in number of iterations.
	Time uint32
	// Defines the memory usage (KiB).
	Memory uint32
	// Defines the number of parallel threads.
	Threads uint8
	// Defines the length of the hash in bytes.
	Length uint32
}

// Encode turns a plain-text password into a hash.
func (h *Argon2Hasher) Encode(password string, salt string) (string, error) {
	bSalt := []byte(salt)
	hash := argon2.Key([]byte(password), bSalt, h.Time, h.Memory, h.Threads, h.Length)

	b64Salt := base64.RawStdEncoding.EncodeToString(bSalt)
	b64Hash := base64.RawStdEncoding.EncodeToString(hash)

	s := fmt.Sprintf("%s$%s$v=%d$m=%d,t=%d,p=%d$%s$%s",
		h.Algorithm,
		"argon2i",
		argon2.Version,
		h.Memory,
		h.Time,
		h.Threads,
		b64Salt,
		b64Hash,
	)

	return s, nil
}

// Verify if a plain-text password matches the encoded digest.
func (h *Argon2Hasher) Verify(password string, encoded string) (bool, error) {
	s := strings.Split(encoded, "$")

	if len(s) != 6 {
		return false, ErrHashComponentMismatch
	}

	algorithm, method, version, params, salt, hash := s[0], s[1], s[2], s[3], s[4], s[5]

	if algorithm != h.Algorithm || method != "argon2i" {
		return false, ErrAlgorithmMismatch
	}

	var v int
	var err error

	_, err = fmt.Sscanf(version, "v=%d", &v)

	if err != nil {
		return false, ErrHashComponentUnreadable
	}

	if v != argon2.Version {
		return false, ErrIncompatibleVersion
	}

	_, err = fmt.Sscanf(params, "m=%d,t=%d,p=%d", &h.Memory, &h.Time, &h.Threads)

	if err != nil {
		return false, ErrHashComponentUnreadable
	}

	bSalt, err := base64.RawStdEncoding.DecodeString(salt)
	bHash, err := base64.RawStdEncoding.DecodeString(hash)

	newHash := argon2.Key([]byte(password), bSalt, h.Time, h.Memory, h.Threads, h.Length)

	return subtle.ConstantTimeCompare(bHash, newHash) == 1, nil
}

// NewArgon2Hasher secures password hashing using the argon2 algorithm.
func NewArgon2Hasher() *Argon2Hasher {
	return &Argon2Hasher{
		Algorithm: "argon2",
		Time:      2,
		Memory:    512,
		Threads:   2,
		Length:    16,
	}
}
<commit_msg>Check b64 errors.<commit_after>package argon2

import (
	"crypto/subtle"
	"encoding/base64"
	"errors"
	"fmt"
	"strings"

	"golang.org/x/crypto/argon2"
)

// Errors returned by Argon2Hasher.
var (
	ErrHashComponentUnreadable = errors.New("unchained/argon2: unreadable component in hashed password")
	ErrHashComponentMismatch   = errors.New("unchained/argon2: hashed password components mismatch")
	ErrAlgorithmMismatch       = errors.New("unchained/argon2: algorithm mismatch")
	ErrIncompatibleVersion     = errors.New("unchained/argon2: incompatible version")
)

// Argon2Hasher implements Argon2i password hasher.
type Argon2Hasher struct {
	// Algorithm identifier.
	Algorithm string
	// Defines the amount of computation time, given in number of iterations.
	Time uint32
	// Defines the memory usage (KiB).
	Memory uint32
	// Defines the number of parallel threads.
	Threads uint8
	// Defines the length of the hash in bytes.
	Length uint32
}

// Encode turns a plain-text password into a hash.
func (h *Argon2Hasher) Encode(password string, salt string) (string, error) {
	bSalt := []byte(salt)
	hash := argon2.Key([]byte(password), bSalt, h.Time, h.Memory, h.Threads, h.Length)

	b64Salt := base64.RawStdEncoding.EncodeToString(bSalt)
	b64Hash := base64.RawStdEncoding.EncodeToString(hash)

	s := fmt.Sprintf("%s$%s$v=%d$m=%d,t=%d,p=%d$%s$%s",
		h.Algorithm,
		"argon2i",
		argon2.Version,
		h.Memory,
		h.Time,
		h.Threads,
		b64Salt,
		b64Hash,
	)

	return s, nil
}

// Verify if a plain-text password matches the encoded digest.
func (h *Argon2Hasher) Verify(password string, encoded string) (bool, error) {
	s := strings.Split(encoded, "$")

	if len(s) != 6 {
		return false, ErrHashComponentMismatch
	}

	algorithm, method, version, params, salt, hash := s[0], s[1], s[2], s[3], s[4], s[5]

	if algorithm != h.Algorithm || method != "argon2i" {
		return false, ErrAlgorithmMismatch
	}

	var v int
	var err error

	_, err = fmt.Sscanf(version, "v=%d", &v)

	if err != nil {
		return false, ErrHashComponentUnreadable
	}

	if v != argon2.Version {
		return false, ErrIncompatibleVersion
	}

	_, err = fmt.Sscanf(params, "m=%d,t=%d,p=%d", &h.Memory, &h.Time, &h.Threads)

	if err != nil {
		return false, ErrHashComponentUnreadable
	}

	bSalt, err := base64.RawStdEncoding.DecodeString(salt)

	if err != nil {
		return false, ErrHashComponentUnreadable
	}

	bHash, err := base64.RawStdEncoding.DecodeString(hash)

	if err != nil {
		return false, ErrHashComponentUnreadable
	}

	newHash := argon2.Key([]byte(password), bSalt, h.Time, h.Memory, h.Threads, h.Length)

	return subtle.ConstantTimeCompare(bHash, newHash) == 1, nil
}

// NewArgon2Hasher secures password hashing using the argon2 algorithm.
func NewArgon2Hasher() *Argon2Hasher {
	return &Argon2Hasher{
		Algorithm: "argon2",
		Time:      2,
		Memory:    512,
		Threads:   2,
		Length:    16,
	}
}
<|endoftext|>


<commit_before>// simplifies file access and adds a simple caching method
package cachedfile

import (
	"fmt"
	"simonwaldherr.de/go/golibs/cache"
	"simonwaldherr.de/go/golibs/file"
	"time"
)

var fileCache *cache.Cache
var cacheInit bool

func cacheWorker(filename string, value interface{}) {
	_, mtime, _, err := file.Time(filename)
	modify := mtime.UnixNano()
	if err == nil && modify < fileCache.Time(filename).UnixNano() {
		file.Write(filename, fmt.Sprint(value), false)
	}
}

func Init(expirationTime, cleanupInterval time.Duration) {
	if cacheInit == true {
		fileCache.DeleteAllWithFunc(cacheWorker)
	}
	cacheInit = true
	fileCache = cache.New2(expirationTime, cleanupInterval, cacheWorker)
}

func Read(filename string) (string, error) {
	if cacheInit == false {
		cacheInit = true
		fileCache = cache.New2(15*time.Minute, 1*time.Minute, cacheWorker)
	}
	var err error
	var data string
	filename, err = file.GetAbsolutePath(filename)
	if err != nil {
		return "", err
	}
	if xdata := fileCache.Get(filename); xdata == nil {
		if data, err = file.Read(filename); err != nil {
			return "", err
		}
		_, mtime, _, err := file.Time(filename)
		duration, _ := time.ParseDuration("2h30m")
		fileCache.SetWithDuration(filename, data, mtime, duration)
	} else {
		data = fmt.Sprint(xdata)
	}
	return data, nil
}

func Write(filename, str string, append bool) error {
	if cacheInit == false {
		cacheInit = true
		fileCache = cache.New2(15*time.Minute, 1*time.Minute, cacheWorker)
	}
	var err error
	var data string
	filename, err = file.GetAbsolutePath(filename)
	if err != nil {
		return err
	}
	if append {
		data, err = Read(filename)
		if err != nil {
			return err
		}
		fileCache.Set(filename, data+str)
	} else {
		fileCache.Set(filename, str)
	}
	return nil
}

func Size(filename string) (int64, error) {
	str, err := Read(filename)

	if err != nil {
		return -1, err
	}

	return int64(len(str)), nil
}

func Clean(filename string) error {
	return Write(filename, "", false)
}

func Stop() {
	if cacheInit == true {
		fileCache.DeleteAllWithFunc(cacheWorker)
	}
}

func Reset() {
	fileCache = cache.New2(15*time.Minute, 1*time.Minute, cacheWorker)
	cacheInit = false
}
<commit_msg>fix cachedfile.Read<commit_after>// simplifies file access and adds a simple caching method
package cachedfile

import (
	"fmt"
	"simonwaldherr.de/go/golibs/cache"
	"simonwaldherr.de/go/golibs/file"
	"time"
)

var fileCache *cache.Cache
var cacheInit bool

func cacheWorker(filename string, value interface{}) {
	_, mtime, _, err := file.Time(filename)
	modify := mtime.UnixNano()
	if err == nil && modify < fileCache.Time(filename).UnixNano() {
		file.Write(filename, fmt.Sprint(value), false)
	}
}

func Init(expirationTime, cleanupInterval time.Duration) {
	if cacheInit == true {
		fileCache.DeleteAllWithFunc(cacheWorker)
	}
	cacheInit = true
	fileCache = cache.New2(expirationTime, cleanupInterval, cacheWorker)
}

func Read(filename string) (string, error) {
	if cacheInit == false {
		cacheInit = true
		fileCache = cache.New2(15*time.Minute, 1*time.Minute, cacheWorker)
	}
	var err error
	var data string
	filename, err = file.GetAbsolutePath(filename)
	if err != nil {
		return "", err
	}
	if xdata := fileCache.Get(filename); xdata == nil {
		if data, err = file.Read(filename); err != nil {
			return "", err
		}
		_, mtime, _, err := file.Time(filename)
		if err != nil {
			mtime = time.Now()
		}
		duration, _ := time.ParseDuration("2h30m")
		fileCache.SetWithDuration(filename, data, mtime, duration)
	} else {
		data = fmt.Sprint(xdata)
	}
	return data, nil
}

func Write(filename, str string, append bool) error {
	if cacheInit == false {
		cacheInit = true
		fileCache = cache.New2(15*time.Minute, 1*time.Minute, cacheWorker)
	}
	var err error
	var data string
	filename, err = file.GetAbsolutePath(filename)
	if err != nil {
		return err
	}
	if append {
		data, err = Read(filename)
		if err != nil {
			return err
		}
		fileCache.Set(filename, data+str)
	} else {
		fileCache.Set(filename, str)
	}
	return nil
}

func Size(filename string) (int64, error) {
	str, err := Read(filename)

	if err != nil {
		return -1, err
	}

	return int64(len(str)), nil
}

func Clean(filename string) error {
	return Write(filename, "", false)
}

func Stop() {
	if cacheInit == true {
		fileCache.DeleteAllWithFunc(cacheWorker)
	}
}

func Reset() {
	fileCache = cache.New2(15*time.Minute, 1*time.Minute, cacheWorker)
	cacheInit = false
}
<|endoftext|>


<commit_before>package agent

import (
	"fmt"
	"strings"
	"time"

	log "github.com/golang/glog"

	"github.com/coreos/coreinit/event"
	"github.com/coreos/coreinit/job"
	"github.com/coreos/coreinit/machine"
	"github.com/coreos/coreinit/registry"
	"github.com/coreos/coreinit/unit"
)

const (
	DefaultServiceTTL = "2s"
	DefaultMachineTTL = "10s"
	refreshInterval   = 2 // Refresh TTLs at 1/2 the TTL length
)

// The Agent owns all of the coordination between the Registry, the local
// Machine, and the local SystemdManager.
type Agent struct {
	Registry   *registry.Registry
	events     *event.EventBus
	Manager    *unit.SystemdManager
	Machine    *machine.Machine
	ServiceTTL string
	state      *AgentState

	// channel used to shutdown any open connections the Agent holds
	stop chan bool
}

func New(registry *registry.Registry, events *event.EventBus, machine *machine.Machine, ttl string, unitPrefix string) *Agent {
	mgr := unit.NewSystemdManager(machine, unitPrefix)

	if ttl == "" {
		ttl = DefaultServiceTTL
	}

	return &Agent{registry, events, mgr, machine, ttl, nil, make(chan bool)}
}

// Trigger all async processes the Agent intends to run
func (a *Agent) Run() {
	a.state = NewState()

	// Kick off the three threads we need for our async processes
	svcstop := a.StartServiceHeartbeatThread()
	machstop := a.StartMachineHeartbeatThread()

	a.events.AddListener("agent", a.Machine, a)

	// Block until we receive a stop signal
	<-a.stop

	// Signal each of the threads we started to also stop
	svcstop <- true
	machstop <- true

	a.events.RemoveListener("agent", a.Machine)

	a.state = nil
}

// Stop all async processes the Agent is running
func (a *Agent) Stop() {
	a.stop <- true
}

// Keep the local statistics in the Registry up to date
func (a *Agent) StartMachineHeartbeatThread() chan bool {
	stop := make(chan bool)
	ttl := parseDuration(DefaultMachineTTL)

	heartbeat := func() {
		a.Registry.SetMachineState(a.Machine, ttl)
	}

	loop := func() {
		interval := intervalFromTTL(DefaultMachineTTL)
		c := time.Tick(interval)
		for _ = range c {
			log.V(1).Info("MachineHeartbeat tick")
			select {
			case <-stop:
				log.V(1).Info("MachineHeartbeat exiting due to stop signal")
				return
			default:
				log.V(1).Info("MachineHeartbeat running")
				heartbeat()
			}
		}
	}

	go loop()
	return stop
}

// Keep the state of local units in the Registry up to date
func (a *Agent) StartServiceHeartbeatThread() chan bool {
	stop := make(chan bool)

	heartbeat := func() {
		localJobs := a.Manager.GetJobs()
		ttl := parseDuration(a.ServiceTTL)
		for _, j := range localJobs {
			if tgt := a.Registry.GetJobTarget(j.Name); tgt != nil && tgt.BootId == a.Machine.BootId {
				log.V(1).Infof("Reporting state of Job(%s)", j.Name)
				a.Registry.SaveJobState(&j, ttl)
			} else {
				log.Infof("Local Job(%s) does not appear to be scheduled to this Machine(%s), stopping it", j.Name, a.Machine.BootId)
				a.Manager.StopJob(&j)
			}
		}
	}

	loop := func() {
		interval := intervalFromTTL(a.ServiceTTL)
		c := time.Tick(interval)
		for _ = range c {
			log.V(1).Info("ServiceHeartbeat tick")
			select {
			case <-stop:
				log.V(1).Info("ServiceHeartbeat exiting due to stop signal")
				return
			default:
				log.V(1).Info("ServiceHeartbeat running")
				heartbeat()
			}
		}
	}

	go loop()
	return stop
}

// Determine whether a given Job conflicts with any other relevant Jobs
// Only call this from a locked AgentState context
func (a *Agent) hasConflict(j *job.Job) bool {
	var reqString string
	for key, slice := range j.Payload.Requirements {
		reqString += fmt.Sprintf("%s = [", key)
		for _, val := range slice {
			reqString += fmt.Sprintf("%s, ", val)
		}
		reqString += fmt.Sprint("] ")
	}

	if len(reqString) > 0 {
		log.V(1).Infof("Job(%s) has requirements %s", j.Name, reqString)
	} else {
		log.V(1).Infof("Job(%s) has no requirements", j.Name)
	}

	isSingleton := func(j *job.Job) bool {
		singleton, ok := j.Payload.Requirements["MachineSingleton"]
		return ok && singleton[0] == "true"
	}

	hasProvides := func(j *job.Job) bool {
		provides, ok := j.Payload.Requirements["Provides"]
		return ok && len(provides) > 0
	}

	if !isSingleton(j) {
		log.V(1).Infof("Job(%s) is not a singleton, therefore no conflict", j.Name)
		return false
	}

	if !hasProvides(j) {
		log.V(1).Infof("Job(%s) does not provide anything, therefore no conflict", j.Name)
		return false
	}

	// Check for conflicts with locally-scheduled jobs
	for _, other := range a.Registry.GetAllJobsByMachine(a.Machine) {
		if !hasProvides(&other) {
			continue
		}

		// Skip self
		if other.Name == j.Name {
			continue
		}

		for _, provide := range j.Payload.Requirements["Provides"] {
			for _, otherProvide := range other.Payload.Requirements["Provides"] {
				if provide == otherProvide {
					log.V(1).Infof("Local Job(%s) already provides '%s'", other.Name, provide)
					return true
				}
			}
		}
	}

	for _, offer := range a.state.GetBadeOffers() {
		// Skip self
		if offer.Job.Name == j.Name {
			continue
		}

		if !hasProvides(&offer.Job) {
			log.V(1).Infof("Outstanding JobBid(%s) does not provide anything, therefore no conflict", offer.Job.Name)
			continue
		}

		for _, provide := range j.Payload.Requirements["Provides"] {
			for _, offerProvide := range offer.Job.Payload.Requirements["Provides"] {
				if provide == offerProvide {
					log.V(1).Infof("Outstanding JobBid(%s) already provides '%s'", offer.Job.Name, provide)
					return true
				}
			}
		}
	}

	return false
}

func (a *Agent) HandleEventJobOffered(ev event.Event) {
	jo := ev.Payload.(job.JobOffer)
	log.V(1).Infof("EventJobOffered(%s): verifying ability to run Job", jo.Job.Name)

	a.state.Lock()
	defer a.state.Unlock()

	// Everything we check against could change over time, so we track all
	// offers starting here for future bidding even if we can't bid now
	a.state.TrackOffer(jo)
	a.state.TrackJobPeers(jo.Job.Name, jo.Job.Payload.Peers)

	metadata := extractMachineMetadata(jo.Job.Payload.Requirements)
	if !a.Machine.HasMetadata(metadata) {
		log.V(1).Infof("EventJobOffered(%s): local Machine Metadata insufficient", jo.Job.Name)
		return
	}

	if a.hasConflict(&jo.Job) {
		log.V(1).Infof("EventJobOffered(%s): local Job conflict, ignoring offer", jo.Job.Name)
		return
	}

	if !a.hasAllLocalPeers(&jo.Job) {
		log.V(1).Infof("EventJobOffered(%s): necessary peer Jobs are not running locally", jo.Job.Name)
		return
	}

	log.Infof("EventJobOffered(%s): passed all criteria, submitting JobBid", jo.Job.Name)
	jb := job.NewBid(jo.Job.Name, a.Machine.BootId)
	a.Registry.SubmitJobBid(jb)
	a.state.TrackBid(jo.Job.Name)
}

func (a *Agent) hasAllLocalPeers(j *job.Job) bool {
	for _, peerName := range j.Payload.Peers {
		log.V(1).Infof("Looking for target of Peer(%s)", peerName)

		//FIXME: ideally the machine would use its own knowledge rather than calling GetJobTarget
		if tgt := a.Registry.GetJobTarget(peerName); tgt == nil || tgt.BootId != a.Machine.BootId {
			log.V(1).Infof("Peer(%s) of Job(%s) not scheduled here", peerName, j.Name)
			return false
		} else {
			log.V(1).Infof("Peer(%s) of Job(%s) scheduled here", peerName, j.Name)
		}
	}
	return true
}

func extractMachineMetadata(requirements map[string][]string) map[string][]string {
	metadata := make(map[string][]string)

	for key, values := range requirements {
		if !strings.HasPrefix(key, "Machine-") {
			log.V(2).Infof("Skipping requirement %s, not machine metadata.", key)
			continue
		}

		// Strip off leading 'Machine-'
		key = key[8:]

		if len(values) == 0 {
			log.V(2).Infof("Metadata(%s) requirement provided no values, ignoring.", key)
			continue
		}

		metadata[key] = values
	}

	return metadata
}

func (a *Agent) HandleEventJobScheduled(ev event.Event) {
	jobName := ev.Payload.(string)

	a.state.Lock()
	defer a.state.Unlock()

	a.state.DropOffer(jobName)
	a.state.DropBid(jobName)

	if ev.Context.BootId != a.Machine.BootId {
		log.V(1).Infof("EventJobScheduled(%s): Job not scheduled to this Agent", jobName)
		a.bidForPossibleOffers()
		return
	} else {
		log.V(1).Infof("EventJobScheduled(%s): Job scheduled to this Agent", jobName)
	}

	log.V(1).Infof("EventJobScheduled(%s): Fetching Job from Registry", jobName)
	j := a.Registry.GetJob(jobName)

	if j == nil {
		log.V(1).Infof("EventJobScheduled(%s): Job not found in Registry")
		return
	}

	// Reassert there are no conflicts
	if a.hasConflict(j) {
		log.V(1).Infof("EventJobScheduled(%s): Local conflict found, cancelling Job", jobName)
		a.Registry.CancelJob(jobName)
		return
	}

	log.Infof("EventJobScheduled(%s): Starting Job", j.Name)
	a.Manager.StartJob(j)

	reversePeers := a.state.GetJobsByPeer(jobName)
	for _, peer := range reversePeers {
		log.V(1).Infof("EventJobScheduled(%s): Found unresolved offer for Peer(%s)", jobName, peer)

		if peerJob := a.Registry.GetJob(peer); !a.hasConflict(peerJob) {
			log.Infof("EventJobScheduled(%s): Submitting JobBid for Peer(%s)", jobName, peer)
			jb := job.NewBid(peer, a.Machine.BootId)
			a.Registry.SubmitJobBid(jb)

			a.state.TrackBid(jb.JobName)
		} else {
			log.V(1).Infof("EventJobScheduled(%s): Would submit JobBid for Peer(%s), but local conflict exists", jobName, peer)
		}
	}
}

// Only call this from a locked AgentState context
func (a *Agent) bidForPossibleOffers() {
	for _, offer := range a.state.GetUnbadeOffers() {
		if !a.hasConflict(&offer.Job) && a.hasAllLocalPeers(&offer.Job) {
			log.Infof("Unscheduled Job(%s) has no local conflicts, submitting JobBid", offer.Job.Name)
			jb := job.NewBid(offer.Job.Name, a.Machine.BootId)
			a.Registry.SubmitJobBid(jb)

			a.state.TrackBid(jb.JobName)
		}
	}
}

func (a *Agent) HandleEventJobCancelled(ev event.Event) {
	//TODO(bcwaldon): We should check the context of the event before
	// making any changes to local systemd or the registry

	jobName := ev.Payload.(string)
	log.Infof("EventJobCancelled(%s): stopping Job", jobName)
	j := job.NewJob(jobName, nil, nil)
	a.Manager.StopJob(j)

	a.state.Lock()
	defer a.state.Unlock()

	reversePeers := a.state.GetJobsByPeer(jobName)
	a.state.DropPeersJob(jobName)

	for _, peer := range reversePeers {
		log.Infof("EventJobCancelled(%s): cancelling Peer(%s) of Job", jobName, peer)
		a.Registry.CancelJob(peer)
	}

	a.bidForPossibleOffers()
}

func parseDuration(d string) time.Duration {
	duration, err := time.ParseDuration(d)
	if err != nil {
		panic(err)
	}

	return duration
}

func intervalFromTTL(ttl string) time.Duration {
	duration := parseDuration(ttl)
	return duration / refreshInterval
}
<commit_msg>refactor(agent): un-export Agent.Registry<commit_after>package agent

import (
	"fmt"
	"strings"
	"time"

	log "github.com/golang/glog"

	"github.com/coreos/coreinit/event"
	"github.com/coreos/coreinit/job"
	"github.com/coreos/coreinit/machine"
	"github.com/coreos/coreinit/registry"
	"github.com/coreos/coreinit/unit"
)

const (
	DefaultServiceTTL = "2s"
	DefaultMachineTTL = "10s"
	refreshInterval   = 2 // Refresh TTLs at 1/2 the TTL length
)

// The Agent owns all of the coordination between the Registry, the local
// Machine, and the local SystemdManager.
type Agent struct {
	registry   *registry.Registry
	events     *event.EventBus
	Manager    *unit.SystemdManager
	Machine    *machine.Machine
	ServiceTTL string
	state      *AgentState

	// channel used to shutdown any open connections the Agent holds
	stop chan bool
}

func New(registry *registry.Registry, events *event.EventBus, machine *machine.Machine, ttl string, unitPrefix string) *Agent {
	mgr := unit.NewSystemdManager(machine, unitPrefix)

	if ttl == "" {
		ttl = DefaultServiceTTL
	}

	return &Agent{registry, events, mgr, machine, ttl, nil, make(chan bool)}
}

// Trigger all async processes the Agent intends to run
func (a *Agent) Run() {
	a.state = NewState()

	// Kick off the three threads we need for our async processes
	svcstop := a.StartServiceHeartbeatThread()
	machstop := a.StartMachineHeartbeatThread()

	a.events.AddListener("agent", a.Machine, a)

	// Block until we receive a stop signal
	<-a.stop

	// Signal each of the threads we started to also stop
	svcstop <- true
	machstop <- true

	a.events.RemoveListener("agent", a.Machine)

	a.state = nil
}

// Stop all async processes the Agent is running
func (a *Agent) Stop() {
	a.stop <- true
}

// Keep the local statistics in the Registry up to date
func (a *Agent) StartMachineHeartbeatThread() chan bool {
	stop := make(chan bool)
	ttl := parseDuration(DefaultMachineTTL)

	heartbeat := func() {
		a.registry.SetMachineState(a.Machine, ttl)
	}

	loop := func() {
		interval := intervalFromTTL(DefaultMachineTTL)
		c := time.Tick(interval)
		for _ = range c {
			log.V(1).Info("MachineHeartbeat tick")
			select {
			case <-stop:
				log.V(1).Info("MachineHeartbeat exiting due to stop signal")
				return
			default:
				log.V(1).Info("MachineHeartbeat running")
				heartbeat()
			}
		}
	}

	go loop()
	return stop
}

// Keep the state of local units in the Registry up to date
func (a *Agent) StartServiceHeartbeatThread() chan bool {
	stop := make(chan bool)

	heartbeat := func() {
		localJobs := a.Manager.GetJobs()
		ttl := parseDuration(a.ServiceTTL)
		for _, j := range localJobs {
			if tgt := a.registry.GetJobTarget(j.Name); tgt != nil && tgt.BootId == a.Machine.BootId {
				log.V(1).Infof("Reporting state of Job(%s)", j.Name)
				a.registry.SaveJobState(&j, ttl)
			} else {
				log.Infof("Local Job(%s) does not appear to be scheduled to this Machine(%s), stopping it", j.Name, a.Machine.BootId)
				a.Manager.StopJob(&j)
			}
		}
	}

	loop := func() {
		interval := intervalFromTTL(a.ServiceTTL)
		c := time.Tick(interval)
		for _ = range c {
			log.V(1).Info("ServiceHeartbeat tick")
			select {
			case <-stop:
				log.V(1).Info("ServiceHeartbeat exiting due to stop signal")
				return
			default:
				log.V(1).Info("ServiceHeartbeat running")
				heartbeat()
			}
		}
	}

	go loop()
	return stop
}

// Determine whether a given Job conflicts with any other relevant Jobs
// Only call this from a locked AgentState context
func (a *Agent) hasConflict(j *job.Job) bool {
	var reqString string
	for key, slice := range j.Payload.Requirements {
		reqString += fmt.Sprintf("%s = [", key)
		for _, val := range slice {
			reqString += fmt.Sprintf("%s, ", val)
		}
		reqString += fmt.Sprint("] ")
	}

	if len(reqString) > 0 {
		log.V(1).Infof("Job(%s) has requirements %s", j.Name, reqString)
	} else {
		log.V(1).Infof("Job(%s) has no requirements", j.Name)
	}

	isSingleton := func(j *job.Job) bool {
		singleton, ok := j.Payload.Requirements["MachineSingleton"]
		return ok && singleton[0] == "true"
	}

	hasProvides := func(j *job.Job) bool {
		provides, ok := j.Payload.Requirements["Provides"]
		return ok && len(provides) > 0
	}

	if !isSingleton(j) {
		log.V(1).Infof("Job(%s) is not a singleton, therefore no conflict", j.Name)
		return false
	}

	if !hasProvides(j) {
		log.V(1).Infof("Job(%s) does not provide anything, therefore no conflict", j.Name)
		return false
	}

	// Check for conflicts with locally-scheduled jobs
	for _, other := range a.registry.GetAllJobsByMachine(a.Machine) {
		if !hasProvides(&other) {
			continue
		}

		// Skip self
		if other.Name == j.Name {
			continue
		}

		for _, provide := range j.Payload.Requirements["Provides"] {
			for _, otherProvide := range other.Payload.Requirements["Provides"] {
				if provide == otherProvide {
					log.V(1).Infof("Local Job(%s) already provides '%s'", other.Name, provide)
					return true
				}
			}
		}
	}

	for _, offer := range a.state.GetBadeOffers() {
		// Skip self
		if offer.Job.Name == j.Name {
			continue
		}

		if !hasProvides(&offer.Job) {
			log.V(1).Infof("Outstanding JobBid(%s) does not provide anything, therefore no conflict", offer.Job.Name)
			continue
		}

		for _, provide := range j.Payload.Requirements["Provides"] {
			for _, offerProvide := range offer.Job.Payload.Requirements["Provides"] {
				if provide == offerProvide {
					log.V(1).Infof("Outstanding JobBid(%s) already provides '%s'", offer.Job.Name, provide)
					return true
				}
			}
		}
	}

	return false
}

func (a *Agent) HandleEventJobOffered(ev event.Event) {
	jo := ev.Payload.(job.JobOffer)
	log.V(1).Infof("EventJobOffered(%s): verifying ability to run Job", jo.Job.Name)

	a.state.Lock()
	defer a.state.Unlock()

	// Everything we check against could change over time, so we track all
	// offers starting here for future bidding even if we can't bid now
	a.state.TrackOffer(jo)
	a.state.TrackJobPeers(jo.Job.Name, jo.Job.Payload.Peers)

	metadata := extractMachineMetadata(jo.Job.Payload.Requirements)
	if !a.Machine.HasMetadata(metadata) {
		log.V(1).Infof("EventJobOffered(%s): local Machine Metadata insufficient", jo.Job.Name)
		return
	}

	if a.hasConflict(&jo.Job) {
		log.V(1).Infof("EventJobOffered(%s): local Job conflict, ignoring offer", jo.Job.Name)
		return
	}

	if !a.hasAllLocalPeers(&jo.Job) {
		log.V(1).Infof("EventJobOffered(%s): necessary peer Jobs are not running locally", jo.Job.Name)
		return
	}

	log.Infof("EventJobOffered(%s): passed all criteria, submitting JobBid", jo.Job.Name)
	jb := job.NewBid(jo.Job.Name, a.Machine.BootId)
	a.registry.SubmitJobBid(jb)
	a.state.TrackBid(jo.Job.Name)
}

func (a *Agent) hasAllLocalPeers(j *job.Job) bool {
	for _, peerName := range j.Payload.Peers {
		log.V(1).Infof("Looking for target of Peer(%s)", peerName)

		//FIXME: ideally the machine would use its own knowledge rather than calling GetJobTarget
		if tgt := a.registry.GetJobTarget(peerName); tgt == nil || tgt.BootId != a.Machine.BootId {
			log.V(1).Infof("Peer(%s) of Job(%s) not scheduled here", peerName, j.Name)
			return false
		} else {
			log.V(1).Infof("Peer(%s) of Job(%s) scheduled here", peerName, j.Name)
		}
	}
	return true
}

func extractMachineMetadata(requirements map[string][]string) map[string][]string {
	metadata := make(map[string][]string)

	for key, values := range requirements {
		if !strings.HasPrefix(key, "Machine-") {
			log.V(2).Infof("Skipping requirement %s, not machine metadata.", key)
			continue
		}

		// Strip off leading 'Machine-'
		key = key[8:]

		if len(values) == 0 {
			log.V(2).Infof("Metadata(%s) requirement provided no values, ignoring.", key)
			continue
		}

		metadata[key] = values
	}

	return metadata
}

func (a *Agent) HandleEventJobScheduled(ev event.Event) {
	jobName := ev.Payload.(string)

	a.state.Lock()
	defer a.state.Unlock()

	a.state.DropOffer(jobName)
	a.state.DropBid(jobName)

	if ev.Context.BootId != a.Machine.BootId {
		log.V(1).Infof("EventJobScheduled(%s): Job not scheduled to this Agent", jobName)
		a.bidForPossibleOffers()
		return
	} else {
		log.V(1).Infof("EventJobScheduled(%s): Job scheduled to this Agent", jobName)
	}

	log.V(1).Infof("EventJobScheduled(%s): Fetching Job from Registry", jobName)
	j := a.registry.GetJob(jobName)

	if j == nil {
		log.V(1).Infof("EventJobScheduled(%s): Job not found in Registry")
		return
	}

	// Reassert there are no conflicts
	if a.hasConflict(j) {
		log.V(1).Infof("EventJobScheduled(%s): Local conflict found, cancelling Job", jobName)
		a.registry.CancelJob(jobName)
		return
	}

	log.Infof("EventJobScheduled(%s): Starting Job", j.Name)
	a.Manager.StartJob(j)

	reversePeers := a.state.GetJobsByPeer(jobName)
	for _, peer := range reversePeers {
		log.V(1).Infof("EventJobScheduled(%s): Found unresolved offer for Peer(%s)", jobName, peer)

		if peerJob := a.registry.GetJob(peer); !a.hasConflict(peerJob) {
			log.Infof("EventJobScheduled(%s): Submitting JobBid for Peer(%s)", jobName, peer)
			jb := job.NewBid(peer, a.Machine.BootId)
			a.registry.SubmitJobBid(jb)

			a.state.TrackBid(jb.JobName)
		} else {
			log.V(1).Infof("EventJobScheduled(%s): Would submit JobBid for Peer(%s), but local conflict exists", jobName, peer)
		}
	}
}

// Only call this from a locked AgentState context
func (a *Agent) bidForPossibleOffers() {
	for _, offer := range a.state.GetUnbadeOffers() {
		if !a.hasConflict(&offer.Job) && a.hasAllLocalPeers(&offer.Job) {
			log.Infof("Unscheduled Job(%s) has no local conflicts, submitting JobBid", offer.Job.Name)
			jb := job.NewBid(offer.Job.Name, a.Machine.BootId)
			a.registry.SubmitJobBid(jb)

			a.state.TrackBid(jb.JobName)
		}
	}
}

func (a *Agent) HandleEventJobCancelled(ev event.Event) {
	//TODO(bcwaldon): We should check the context of the event before
	// making any changes to local systemd or the registry

	jobName := ev.Payload.(string)
	log.Infof("EventJobCancelled(%s): stopping Job", jobName)
	j := job.NewJob(jobName, nil, nil)
	a.Manager.StopJob(j)

	a.state.Lock()
	defer a.state.Unlock()

	reversePeers := a.state.GetJobsByPeer(jobName)
	a.state.DropPeersJob(jobName)

	for _, peer := range reversePeers {
		log.Infof("EventJobCancelled(%s): cancelling Peer(%s) of Job", jobName, peer)
		a.registry.CancelJob(peer)
	}

	a.bidForPossibleOffers()
}

func parseDuration(d string) time.Duration {
	duration, err := time.ParseDuration(d)
	if err != nil {
		panic(err)
	}

	return duration
}

func intervalFromTTL(ttl string) time.Duration {
	duration := parseDuration(ttl)
	return duration / refreshInterval
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"log"
	"net"
	"os"
	"os/signal"
	"strconv"

	linuxproc "github.com/c9s/goprocinfo/linux"
)

func get_idle() (out int) {
	stat, err := linuxproc.ReadStat("/proc/stat")
	if err != nil {
		log.Fatal("stat read fail")
	}
	all := stat.CPUStatAll
	total := all.User + all.Nice + all.System + all.Idle + all.IOWait +
		all.IRQ + all.SoftIRQ + all.Steal + all.Guest + all.GuestNice
	idlePercent := 100.00 * (float64(all.Idle) / float64(total))
	return int(idlePercent)
}

func handleTalk(conn net.Conn, command <-chan []byte) {
	//log.Println("in handleTalk")
	defer conn.Close()
	select {
	case msg := <-command:
		conn.Write(msg)
	default:
		idle := strconv.Itoa(get_idle())
		conn.Write([]byte(idle))
		//conn.Close()
	}
	return
}

func handleListen(conn net.Conn, command chan []byte) {
	//log.Println("in handleListen")
	defer conn.Close()
	line, err := bufio.NewReader(conn).ReadBytes('\n')
	if err != nil {
		return
	}
	command <- line
	conn.Write([]byte("OK"))
	return
}

func Talk(ln net.Listener, command chan []byte) {
	//log.Println("in talk")
	defer ln.Close()
	for {
		conn, err := ln.Accept()
		if err != nil {
			log.Println("there was an error:", err)
			break
		}
		go handleTalk(conn, command)
	}
}

func Listen(ln net.Listener, command chan []byte) {
	//log.Println("in listen")
	defer ln.Close()
	for {
		conn, err := ln.Accept()
		if err != nil {
			log.Println("there was an error:", err)
			break
		}
		go handleListen(conn, command)
	}

}

func main() {
	command := make(chan []byte, 1)
	ln, err := net.Listen("tcp", ":5309")
	if err != nil {
		log.Fatalln("there was an error:", err)
	}
	go Talk(ln, command)

	ln2, err := net.Listen("tcp", ":8675")
	if err != nil {
		log.Fatalln("there was an error:", err)
	}
	go Listen(ln2, command)

	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt, os.Kill)
	s := <-c
	log.Println("exiting on:", s)
}
<commit_msg>the listener should only listen on localhost<commit_after>package main

import (
	"bufio"
	"log"
	"net"
	"os"
	"os/signal"
	"strconv"

	linuxproc "github.com/c9s/goprocinfo/linux"
)

func get_idle() (out int) {
	stat, err := linuxproc.ReadStat("/proc/stat")
	if err != nil {
		log.Fatal("stat read fail")
	}
	all := stat.CPUStatAll
	total := all.User + all.Nice + all.System + all.Idle + all.IOWait +
		all.IRQ + all.SoftIRQ + all.Steal + all.Guest + all.GuestNice
	idlePercent := 100.00 * (float64(all.Idle) / float64(total))
	return int(idlePercent)
}

func handleTalk(conn net.Conn, command <-chan []byte) {
	//log.Println("in handleTalk")
	defer conn.Close()
	select {
	case msg := <-command:
		conn.Write(msg)
	default:
		idle := strconv.Itoa(get_idle())
		conn.Write([]byte(idle))
		//conn.Close()
	}
	return
}

func handleListen(conn net.Conn, command chan []byte) {
	//log.Println("in handleListen")
	defer conn.Close()
	line, err := bufio.NewReader(conn).ReadBytes('\n')
	if err != nil {
		return
	}
	command <- line
	conn.Write([]byte("OK"))
	return
}

func Talk(ln net.Listener, command chan []byte) {
	//log.Println("in talk")
	defer ln.Close()
	for {
		conn, err := ln.Accept()
		if err != nil {
			log.Println("there was an error:", err)
			break
		}
		go handleTalk(conn, command)
	}
}

func Listen(ln net.Listener, command chan []byte) {
	//log.Println("in listen")
	defer ln.Close()
	for {
		conn, err := ln.Accept()
		if err != nil {
			log.Println("there was an error:", err)
			break
		}
		go handleListen(conn, command)
	}

}

func main() {
	command := make(chan []byte, 1)
	ln, err := net.Listen("tcp", ":5309")
	if err != nil {
		log.Fatalln("there was an error:", err)
	}
	go Talk(ln, command)

	ln2, err := net.Listen("tcp", "localhost:8675")
	if err != nil {
		log.Fatalln("there was an error:", err)
	}
	go Listen(ln2, command)

	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt, os.Kill)
	s := <-c
	log.Println("exiting on:", s)
}
<|endoftext|>


<commit_before>package e2e

import (
	"fmt"
	"net/http"
	"os"
	"testing"
	"time"

	"github.com/oinume/lekcije/server/model"
	"github.com/stretchr/testify/assert"
)

var _ = time.UTC
var _ = fmt.Print

func TestOAuthGoogle(t *testing.T) {
	if os.Getenv("CIRCLECI") != "" {
		t.Skipf("Skip because it can't render Google log in page.")
	}
	a := assert.New(t)
	driver := newWebDriver()
	err := driver.Start()
	a.Nil(err)
	defer driver.Stop()

	page, err := driver.NewPage()
	a.Nil(err)
	a.Nil(page.Navigate(server.URL))
	//time.Sleep(10 * time.Second)
	link := page.FindByXPath("//div[@class='starter-template']/a")
	u, err := link.Attribute("href")
	a.Nil(err)
	fmt.Printf("u = %v, err = %v\n", u, err)
	link.Click()
	//time.Sleep(10 * time.Second)

	googleAccount := os.Getenv("E2E_GOOGLE_ACCOUNT")
	err = page.Find("#Email").Fill(googleAccount)
	a.Nil(err)
	page.Find("#gaia_loginform").Submit()
	a.Nil(err)

	time.Sleep(time.Second * 1)
	page.Find("#Passwd").Fill(os.Getenv("E2E_GOOGLE_PASSWORD"))
	a.Nil(err)
	page.Find("#gaia_loginform").Submit()
	a.Nil(err)

	time.Sleep(time.Second * 3)
	err = page.Find("#submit_approve_access").Click()
	a.Nil(err)
	//time.Sleep(time.Second * 10)
	// TODO: Check HTML content

	cookies, err := page.GetCookies()
	a.Nil(err)
	apiToken := getAPIToken(cookies)
	a.NotEmpty(apiToken)

	user, err := model.NewUserService(db).FindByUserAPIToken(apiToken)
	a.Nil(err)
	a.Equal(googleAccount, user.Email.Raw())
}

func TestOAuthGoogleLogout(t *testing.T) {
	// TODO: user_api_token will be deleted after logout
}

func getAPIToken(cookies []*http.Cookie) string {
	for _, cookie := range cookies {
		if cookie.Name == "apiToken" {
			return cookie.Value
		}
	}
	return ""
}
<commit_msg>Implement TestOAuthGoogleLogout<commit_after>package e2e

import (
	"fmt"
	"net/http"
	"os"
	"testing"
	"time"

	"net/url"

	"github.com/oinume/lekcije/server/controller"
	"github.com/oinume/lekcije/server/model"
	"github.com/oinume/lekcije/server/util"
	"github.com/stretchr/testify/assert"
)

var _ = time.UTC
var _ = fmt.Print

func TestOAuthGoogle(t *testing.T) {
	if os.Getenv("CIRCLECI") != "" {
		t.Skipf("Skip because it can't render Google log in page.")
	}
	a := assert.New(t)
	driver := newWebDriver()
	err := driver.Start()
	a.Nil(err)
	defer driver.Stop()

	page, err := driver.NewPage()
	a.Nil(err)
	a.Nil(page.Navigate(server.URL))
	//time.Sleep(10 * time.Second)
	link := page.FindByXPath("//div[@class='starter-template']/a")
	u, err := link.Attribute("href")
	a.Nil(err)
	fmt.Printf("u = %v, err = %v\n", u, err)
	link.Click()
	//time.Sleep(10 * time.Second)

	googleAccount := os.Getenv("E2E_GOOGLE_ACCOUNT")
	err = page.Find("#Email").Fill(googleAccount)
	a.Nil(err)
	page.Find("#gaia_loginform").Submit()
	a.Nil(err)

	time.Sleep(time.Second * 1)
	page.Find("#Passwd").Fill(os.Getenv("E2E_GOOGLE_PASSWORD"))
	a.Nil(err)
	page.Find("#gaia_loginform").Submit()
	a.Nil(err)

	time.Sleep(time.Second * 3)
	err = page.Find("#submit_approve_access").Click()
	a.Nil(err)
	//time.Sleep(time.Second * 10)
	// TODO: Check HTML content

	cookies, err := page.GetCookies()
	a.Nil(err)
	apiToken := getAPIToken(cookies)
	a.NotEmpty(apiToken)

	user, err := model.NewUserService(db).FindByUserAPIToken(apiToken)
	a.Nil(err)
	a.Equal(googleAccount, user.Email.Raw())
}

func TestOAuthGoogleLogout(t *testing.T) {
	a := assert.New(t)

	user, apiToken, err := createUserAndLogin("oinume", randomEmail("oinume"), util.RandomString(16))
	a.Nil(err)

	driver := newWebDriver()
	a.Nil(driver.Start())
	defer driver.Stop()

	page, err := driver.NewPage()
	a.Nil(err)
	a.Nil(page.Navigate(server.URL))
	u, err := url.Parse(server.URL)
	a.Nil(err)
	fmt.Printf("server.URL = %v\n", server.URL)
	cookie := &http.Cookie{
		Name:     controller.APITokenCookieName,
		Domain:   u.Host,
		Value:    apiToken,
		Path:     "/",
		Expires:  time.Now().Add(model.UserAPITokenExpiration),
		HttpOnly: false,
	}
	a.Nil(page.SetCookie(cookie))
	a.Nil(page.Navigate(server.URL + "/me"))
	time.Sleep(100 * time.Second)
	a.Nil(user)

	// TODO: user_api_token will be deleted after logout
}

func getAPIToken(cookies []*http.Cookie) string {
	for _, cookie := range cookies {
		if cookie.Name == "apiToken" {
			return cookie.Value
		}
	}
	return ""
}
<|endoftext|>


<commit_before>package worker

import (
	"bytes"
	"log"
	"time"

	"encoding/json"

	"net/http"

	"fmt"

	"github.com/ChristianNorbertBraun/seaweed-banking/seaweed-banking-account-updater/database"
	"github.com/ChristianNorbertBraun/seaweed-banking/seaweed-banking-account-updater/handler"
	"github.com/ChristianNorbertBraun/seaweed-banking/seaweed-banking-account-updater/model"
)

var UpdateTicker *time.Ticker

// SetUpUpdateWorker starts a worker which checks the update table for new entries
// and updates the matching accountInfos
func SetUpUpdateWorker(duration time.Duration) {
	UpdateTicker = time.NewTicker(duration)
	go func() {
		for t := range UpdateTicker.C {
			log.Println("-------------------------------------------------------------")
			log.Println("Start Update at: ", t)

			runUpdate()
		}
	}()
}

// StopTicker stops the given ticker
func StopTicker(ticker *time.Ticker) {
	if ticker != nil {
		ticker.Stop()
	}
}

func runUpdate() {
	updates, err := database.FindAllUpdates()
	if err != nil {
		log.Println("Unable to get updates", err)

		return
	}

	distributeUpdates(updates)
}

func distributeUpdates(updates []*model.Update) {
	numberOfSubscribers := len(subscribers)
	log.Printf("Distributing updates on %d subscrribers", numberOfSubscribers)
	// the master itself is also a worker therefore +1
	numberOfUpdatesPerWorker := len(updates) / (numberOfSubscribers + 1)

	if len(updates) < numberOfSubscribers+1 {
		numberOfUpdatesPerWorker = 1
	}

	start := 0
	for url := range subscribers {
		if start >= len(updates) {
			return
		}
		if err := sendUpdates(url, updates[start:start+numberOfUpdatesPerWorker]); err != nil {
			Unregister(url)
		}
		start = start + numberOfUpdatesPerWorker
	}

	handler.UpdateAccountInfo(updates[start:])
}

func sendUpdates(url string, updates []*model.Update) error {
	buffer := bytes.Buffer{}

	if err := json.NewEncoder(&buffer).Encode(updates); err != nil {
		return err
	}

	resp, err := http.Post(url+"/do/update", "application/json", &buffer)

	if err != nil {
		return err
	}

	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("Unable to send update to %s", url)
	}

	return nil
}
<commit_msg>Insert log<commit_after>package worker

import (
	"bytes"
	"log"
	"time"

	"encoding/json"

	"net/http"

	"fmt"

	"github.com/ChristianNorbertBraun/seaweed-banking/seaweed-banking-account-updater/database"
	"github.com/ChristianNorbertBraun/seaweed-banking/seaweed-banking-account-updater/handler"
	"github.com/ChristianNorbertBraun/seaweed-banking/seaweed-banking-account-updater/model"
)

var UpdateTicker *time.Ticker

// SetUpUpdateWorker starts a worker which checks the update table for new entries
// and updates the matching accountInfos
func SetUpUpdateWorker(duration time.Duration) {
	UpdateTicker = time.NewTicker(duration)
	go func() {
		for t := range UpdateTicker.C {
			log.Println("-------------------------------------------------------------")
			log.Println("Start Update at: ", t)

			runUpdate()
		}
	}()
}

// StopTicker stops the given ticker
func StopTicker(ticker *time.Ticker) {
	if ticker != nil {
		ticker.Stop()
	}
}

func runUpdate() {
	updates, err := database.FindAllUpdates()
	if err != nil {
		log.Println("Unable to get updates", err)

		return
	}

	distributeUpdates(updates)
}

func distributeUpdates(updates []*model.Update) {
	numberOfSubscribers := len(subscribers)
	if numberOfSubscribers == 0 {
		log.Println("No subscribers have to do all the work allone. Pff.")
	} else {
		log.Printf("Distributing updates on %d subscrribers", numberOfSubscribers)
	}
	// the master itself is also a worker therefore +1
	numberOfUpdatesPerWorker := len(updates) / (numberOfSubscribers + 1)

	if len(updates) < numberOfSubscribers+1 {
		numberOfUpdatesPerWorker = 1
	}

	start := 0
	for url := range subscribers {
		if start >= len(updates) {
			return
		}
		if err := sendUpdates(url, updates[start:start+numberOfUpdatesPerWorker]); err != nil {
			Unregister(url)
		}
		start = start + numberOfUpdatesPerWorker
	}

	handler.UpdateAccountInfo(updates[start:])
}

func sendUpdates(url string, updates []*model.Update) error {
	buffer := bytes.Buffer{}

	if err := json.NewEncoder(&buffer).Encode(updates); err != nil {
		return err
	}

	resp, err := http.Post(url+"/do/update", "application/json", &buffer)

	if err != nil {
		return err
	}

	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("Unable to send update to %s", url)
	}

	return nil
}
<|endoftext|>


<commit_before>// Package sflow decodes sFlow packets
//: ----------------------------------------------------------------------------
//: Copyright (C) 2017 Verizon.  All Rights Reserved.
//: All Rights Reserved
//:
//: file:    flow_sample.go
//: details: TODO
//: author:  Mehrdad Arshad Rad
//: date:    02/01/2017
//:
//: 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 sflow

import (
	"errors"
	"io"

	"github.com/VerizonDigital/vflow/packet"
)

const (
	// SFDataRawHeader is sFlow Raw Packet Header number
	SFDataRawHeader = 1

	// SFDataExtSwitch is sFlow Extended Switch Data number
	SFDataExtSwitch = 1001
)

// FlowSample represents single flow sample
type FlowSample struct {
	SequenceNo   uint32 // Incremented with each flow sample
	SourceID     byte   // sfSourceID
	SamplingRate uint32 // sfPacketSamplingRate
	SamplePool   uint32 // Total number of packets that could have been sampled
	Drops        uint32 // Number of times a packet was dropped due to lack of resources
	Input        uint32 // SNMP ifIndex of input interface
	Output       uint32 // SNMP ifIndex of input interface
	RecordsNo    uint32 // Number of records to follow
}

// SampledHeader represents sampled header
type SampledHeader struct {
	Protocol     uint32 // (enum SFLHeader_protocol)
	FrameLength  uint32 // Original length of packet before sampling
	Stripped     uint32 // Header/trailer bytes stripped by sender
	HeaderLength uint32 // Length of sampled header bytes to follow
	Header       []byte // Header bytes
}

// ExtSwitchData represents Extended Switch Data
type ExtSwitchData struct {
	SrcVlan     uint32 // The 802.1Q VLAN id of incoming frame
	SrcPriority uint32 // The 802.1p priority of incoming frame
	DstVlan     uint32 // The 802.1Q VLAN id of outgoing frame
	DstPriority uint32 // The 802.1p priority of outgoing frame
}

var (
	errMaxOutEthernetLength = errors.New("the ethernet lenght is greater than 1500")
)

func (fs *FlowSample) unmarshal(r io.ReadSeeker) error {
	var err error

	if err = read(r, &fs.SequenceNo); err != nil {
		return err
	}

	if err = read(r, &fs.SourceID); err != nil {
		return err
	}

	r.Seek(3, 1) // skip counter sample decoding

	if err = read(r, &fs.SamplingRate); err != nil {
		return err
	}

	if err = read(r, &fs.SamplePool); err != nil {
		return err
	}

	if err = read(r, &fs.Drops); err != nil {
		return err
	}

	if err = read(r, &fs.Input); err != nil {
		return err
	}

	if err = read(r, &fs.Output); err != nil {
		return err
	}

	if err = read(r, &fs.RecordsNo); err != nil {
		return err
	}

	return nil
}

func (sh *SampledHeader) unmarshal(r io.Reader) error {
	var err error

	if err = read(r, &sh.Protocol); err != nil {
		return err
	}

	if err = read(r, &sh.FrameLength); err != nil {
		return err
	}

	if err = read(r, &sh.Stripped); err != nil {
		return err
	}

	if err = read(r, &sh.HeaderLength); err != nil {
		return err
	}

	if sh.HeaderLength > 1500 {
		return errMaxOutEthernetLength
	}

	// cut off a header length mod 4 == 0 number of bytes
	tmp := (4 - sh.HeaderLength) % 4
	if tmp < 0 {
		tmp += 4
	}

	sh.Header = make([]byte, sh.HeaderLength+tmp)
	if _, err = r.Read(sh.Header); err != nil {
		return err
	}

	sh.Header = sh.Header[:sh.HeaderLength]

	return nil
}

func (es *ExtSwitchData) unmarshal(r io.Reader) error {
	var err error

	if err = read(r, &es.SrcVlan); err != nil {
		return err
	}

	if err = read(r, &es.SrcPriority); err != nil {
		return err
	}

	if err = read(r, &es.DstVlan); err != nil {
		return err
	}

	if err = read(r, &es.SrcPriority); err != nil {
		return err
	}

	return nil
}

func decodeFlowSample(r io.ReadSeeker) ([]interface{}, error) {
	var (
		fs          = new(FlowSample)
		data        []interface{}
		rTypeFormat uint32
		rTypeLength uint32
		err         error
	)

	if err = fs.unmarshal(r); err != nil {
		return nil, err
	}

	data = append(data, fs)

	for i := uint32(0); i < fs.RecordsNo; i++ {
		if err = read(r, &rTypeFormat); err != nil {
			return nil, err
		}
		if err = read(r, &rTypeLength); err != nil {
			return nil, err
		}

		switch rTypeFormat {
		case SFDataRawHeader:
			h, err := decodeSampledHeader(r)
			if err != nil {
				return data, err
			}
			data = append(data, h)
		case SFDataExtSwitch:
			d, err := decodeExtSwitchData(r)
			if err != nil {
				return data, err
			}
			data = append(data, d)
		default:
			r.Seek(int64(rTypeLength), 1)
		}
	}

	return data, nil
}

func decodeSampledHeader(r io.Reader) (*packet.Packet, error) {
	var (
		h   = new(SampledHeader)
		err error
	)

	if err = h.unmarshal(r); err != nil {
		return nil, err
	}

	p := packet.NewPacket()
	d, err := p.Decoder(h.Header)
	if err != nil {
		return nil, err
	}

	return d, nil
}

func decodeExtSwitchData(r io.Reader) (*ExtSwitchData, error) {
	var es = new(ExtSwitchData)

	if err := es.unmarshal(r); err != nil {
		return nil, err
	}

	return es, nil
}
<commit_msg>fix typo<commit_after>// Package sflow decodes sFlow packets
//: ----------------------------------------------------------------------------
//: Copyright (C) 2017 Verizon.  All Rights Reserved.
//: All Rights Reserved
//:
//: file:    flow_sample.go
//: details: TODO
//: author:  Mehrdad Arshad Rad
//: date:    02/01/2017
//:
//: 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 sflow

import (
	"errors"
	"io"

	"github.com/VerizonDigital/vflow/packet"
)

const (
	// SFDataRawHeader is sFlow Raw Packet Header number
	SFDataRawHeader = 1

	// SFDataExtSwitch is sFlow Extended Switch Data number
	SFDataExtSwitch = 1001
)

// FlowSample represents single flow sample
type FlowSample struct {
	SequenceNo   uint32 // Incremented with each flow sample
	SourceID     byte   // sfSourceID
	SamplingRate uint32 // sfPacketSamplingRate
	SamplePool   uint32 // Total number of packets that could have been sampled
	Drops        uint32 // Number of times a packet was dropped due to lack of resources
	Input        uint32 // SNMP ifIndex of input interface
	Output       uint32 // SNMP ifIndex of input interface
	RecordsNo    uint32 // Number of records to follow
}

// SampledHeader represents sampled header
type SampledHeader struct {
	Protocol     uint32 // (enum SFLHeader_protocol)
	FrameLength  uint32 // Original length of packet before sampling
	Stripped     uint32 // Header/trailer bytes stripped by sender
	HeaderLength uint32 // Length of sampled header bytes to follow
	Header       []byte // Header bytes
}

// ExtSwitchData represents Extended Switch Data
type ExtSwitchData struct {
	SrcVlan     uint32 // The 802.1Q VLAN id of incoming frame
	SrcPriority uint32 // The 802.1p priority of incoming frame
	DstVlan     uint32 // The 802.1Q VLAN id of outgoing frame
	DstPriority uint32 // The 802.1p priority of outgoing frame
}

var (
	errMaxOutEthernetLength = errors.New("the ethernet length is greater than 1500")
)

func (fs *FlowSample) unmarshal(r io.ReadSeeker) error {
	var err error

	if err = read(r, &fs.SequenceNo); err != nil {
		return err
	}

	if err = read(r, &fs.SourceID); err != nil {
		return err
	}

	r.Seek(3, 1) // skip counter sample decoding

	if err = read(r, &fs.SamplingRate); err != nil {
		return err
	}

	if err = read(r, &fs.SamplePool); err != nil {
		return err
	}

	if err = read(r, &fs.Drops); err != nil {
		return err
	}

	if err = read(r, &fs.Input); err != nil {
		return err
	}

	if err = read(r, &fs.Output); err != nil {
		return err
	}

	if err = read(r, &fs.RecordsNo); err != nil {
		return err
	}

	return nil
}

func (sh *SampledHeader) unmarshal(r io.Reader) error {
	var err error

	if err = read(r, &sh.Protocol); err != nil {
		return err
	}

	if err = read(r, &sh.FrameLength); err != nil {
		return err
	}

	if err = read(r, &sh.Stripped); err != nil {
		return err
	}

	if err = read(r, &sh.HeaderLength); err != nil {
		return err
	}

	if sh.HeaderLength > 1500 {
		return errMaxOutEthernetLength
	}

	// cut off a header length mod 4 == 0 number of bytes
	tmp := (4 - sh.HeaderLength) % 4
	if tmp < 0 {
		tmp += 4
	}

	sh.Header = make([]byte, sh.HeaderLength+tmp)
	if _, err = r.Read(sh.Header); err != nil {
		return err
	}

	sh.Header = sh.Header[:sh.HeaderLength]

	return nil
}

func (es *ExtSwitchData) unmarshal(r io.Reader) error {
	var err error

	if err = read(r, &es.SrcVlan); err != nil {
		return err
	}

	if err = read(r, &es.SrcPriority); err != nil {
		return err
	}

	if err = read(r, &es.DstVlan); err != nil {
		return err
	}

	if err = read(r, &es.SrcPriority); err != nil {
		return err
	}

	return nil
}

func decodeFlowSample(r io.ReadSeeker) ([]interface{}, error) {
	var (
		fs          = new(FlowSample)
		data        []interface{}
		rTypeFormat uint32
		rTypeLength uint32
		err         error
	)

	if err = fs.unmarshal(r); err != nil {
		return nil, err
	}

	data = append(data, fs)

	for i := uint32(0); i < fs.RecordsNo; i++ {
		if err = read(r, &rTypeFormat); err != nil {
			return nil, err
		}
		if err = read(r, &rTypeLength); err != nil {
			return nil, err
		}

		switch rTypeFormat {
		case SFDataRawHeader:
			h, err := decodeSampledHeader(r)
			if err != nil {
				return data, err
			}
			data = append(data, h)
		case SFDataExtSwitch:
			d, err := decodeExtSwitchData(r)
			if err != nil {
				return data, err
			}
			data = append(data, d)
		default:
			r.Seek(int64(rTypeLength), 1)
		}
	}

	return data, nil
}

func decodeSampledHeader(r io.Reader) (*packet.Packet, error) {
	var (
		h   = new(SampledHeader)
		err error
	)

	if err = h.unmarshal(r); err != nil {
		return nil, err
	}

	p := packet.NewPacket()
	d, err := p.Decoder(h.Header)
	if err != nil {
		return nil, err
	}

	return d, nil
}

func decodeExtSwitchData(r io.Reader) (*ExtSwitchData, error) {
	var es = new(ExtSwitchData)

	if err := es.unmarshal(r); err != nil {
		return nil, err
	}

	return es, nil
}
<|endoftext|>


<commit_before>/*
 * This file is part of the KubeVirt project
 *
 * 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.
 *
 * Copyright 2017, 2018 Red Hat, Inc.
 *
 */

package tests_test

import (
	"strings"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	kubev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/resource"

	v1 "kubevirt.io/client-go/api/v1"
	"kubevirt.io/client-go/kubecli"
	"kubevirt.io/kubevirt/tests"
	cd "kubevirt.io/kubevirt/tests/containerdisk"
)

var _ = Describe("[Serial][rfe_id:609]VMIheadless", func() {

	var err error
	var virtClient kubecli.KubevirtClient
	var vmi *v1.VirtualMachineInstance

	BeforeEach(func() {
		virtClient, err = kubecli.GetKubevirtClient()
		tests.PanicOnError(err)

		tests.BeforeTestCleanup()
		vmi = tests.NewRandomVMIWithEphemeralDisk(cd.ContainerDiskFor(cd.ContainerDiskAlpine))
	})

	Describe("[rfe_id:609]Creating a VirtualMachineInstance", func() {

		Context("with headless", func() {

			BeforeEach(func() {
				f := false
				vmi.Spec.Domain.Devices.AutoattachGraphicsDevice = &f
			})

			It("[test_id:707]should create headless vmi without any issue", func() {
				tests.RunVMIAndExpectLaunch(vmi, 30)
			})

			It("[test_id:714][posneg:positive]should not have vnc graphic device in xml", func() {
				tests.RunVMIAndExpectLaunch(vmi, 30)

				runningVMISpec, err := tests.GetRunningVMIDomainSpec(vmi)
				Expect(err).ToNot(HaveOccurred(), "should get vmi spec without problem")

				Expect(len(runningVMISpec.Devices.Graphics)).To(Equal(0), "should not have any graphics devices present")
			})

			It("[test_id:737][posneg:positive]should match memory with overcommit enabled", func() {
				vmi.Spec.Domain.Resources = v1.ResourceRequirements{
					Requests: kubev1.ResourceList{
						kubev1.ResourceMemory: resource.MustParse("100M"),
					},
					OvercommitGuestOverhead: true,
				}
				vmi = tests.RunVMIAndExpectLaunch(vmi, 30)

				readyPod := tests.GetPodByVirtualMachineInstance(vmi, tests.NamespaceTestDefault)
				computeContainer := tests.GetComputeContainerOfPod(readyPod)

				Expect(computeContainer.Resources.Requests.Memory().String()).To(Equal("100M"))
			})

			It("[test_id:2444][posneg:negative]should not match memory with overcommit disabled", func() {
				vmi.Spec.Domain.Resources = v1.ResourceRequirements{
					Requests: kubev1.ResourceList{
						kubev1.ResourceMemory: resource.MustParse("100M"),
					},
				}
				vmi = tests.RunVMIAndExpectLaunch(vmi, 30)

				readyPod := tests.GetPodByVirtualMachineInstance(vmi, tests.NamespaceTestDefault)
				computeContainer := tests.GetComputeContainerOfPod(readyPod)

				Expect(computeContainer.Resources.Requests.Memory().String()).ToNot(Equal("100M"))
			})

			It("[test_id:713]should have more memory on pod when headless", func() {
				normalVmi := tests.NewRandomVMIWithEphemeralDisk(cd.ContainerDiskFor(cd.ContainerDiskAlpine))

				vmi = tests.RunVMIAndExpectLaunch(vmi, 30)
				normalVmi = tests.RunVMIAndExpectLaunch(normalVmi, 30)

				readyPod := tests.GetPodByVirtualMachineInstance(vmi, tests.NamespaceTestDefault)
				computeContainer := tests.GetComputeContainerOfPod(readyPod)

				normalReadyPod := tests.GetPodByVirtualMachineInstance(normalVmi, tests.NamespaceTestDefault)
				normalComputeContainer := tests.GetComputeContainerOfPod(normalReadyPod)

				memDiff := normalComputeContainer.Resources.Requests.Memory()
				memDiff.Sub(*computeContainer.Resources.Requests.Memory())

				Expect(memDiff.ScaledValue(resource.Mega) > 15).To(BeTrue(), "memory difference between headless and normal should be roughly 16M")
			})

			It("[test_id:738][posneg:negative]should not connect to VNC", func() {
				vmi = tests.RunVMIAndExpectLaunch(vmi, 30)

				_, err := virtClient.VirtualMachineInstance(vmi.ObjectMeta.Namespace).VNC(vmi.ObjectMeta.Name)

				Expect(err.Error()).To(Equal("No graphics devices are present."), "vnc should not connect on headless VM")
			})

			It("[test_id:709][posneg:positive]should connect to console", func() {
				vmi = tests.RunVMIAndExpectLaunch(vmi, 30)

				By("checking that console works")
				expecter, err := tests.LoggedInAlpineExpecter(vmi)
				Expect(err).ToNot(HaveOccurred())
				defer expecter.Close()
			})

		})

		Context("without headless", func() {

			It("[test_id:714][posneg:negative]should have one vnc graphic device in xml", func() {
				tests.RunVMIAndExpectLaunch(vmi, 30)

				runningVMISpec, err := tests.GetRunningVMIDomainSpec(vmi)
				Expect(err).ToNot(HaveOccurred(), "should get vmi spec without problem")

				vncCount := 0
				for _, gr := range runningVMISpec.Devices.Graphics {
					if strings.ToLower(gr.Type) == "vnc" {
						vncCount += 1
					}
				}
				Expect(vncCount).To(Equal(1), "should have exactly one VNC device")
			})

		})

	})

})
<commit_msg>Allow the headless service tests for VMIs to run in parallel<commit_after>/*
 * This file is part of the KubeVirt project
 *
 * 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.
 *
 * Copyright 2017, 2018 Red Hat, Inc.
 *
 */

package tests_test

import (
	"strings"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	kubev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/resource"

	v1 "kubevirt.io/client-go/api/v1"
	"kubevirt.io/client-go/kubecli"
	"kubevirt.io/kubevirt/tests"
	cd "kubevirt.io/kubevirt/tests/containerdisk"
)

var _ = Describe("[rfe_id:609]VMIheadless", func() {

	var err error
	var virtClient kubecli.KubevirtClient
	var vmi *v1.VirtualMachineInstance

	BeforeEach(func() {
		virtClient, err = kubecli.GetKubevirtClient()
		tests.PanicOnError(err)

		tests.BeforeTestCleanup()
		vmi = tests.NewRandomVMIWithEphemeralDisk(cd.ContainerDiskFor(cd.ContainerDiskAlpine))
	})

	Describe("[rfe_id:609]Creating a VirtualMachineInstance", func() {

		Context("with headless", func() {

			BeforeEach(func() {
				f := false
				vmi.Spec.Domain.Devices.AutoattachGraphicsDevice = &f
			})

			It("[test_id:707]should create headless vmi without any issue", func() {
				tests.RunVMIAndExpectLaunch(vmi, 30)
			})

			It("[test_id:714][posneg:positive]should not have vnc graphic device in xml", func() {
				tests.RunVMIAndExpectLaunch(vmi, 30)

				runningVMISpec, err := tests.GetRunningVMIDomainSpec(vmi)
				Expect(err).ToNot(HaveOccurred(), "should get vmi spec without problem")

				Expect(len(runningVMISpec.Devices.Graphics)).To(Equal(0), "should not have any graphics devices present")
			})

			It("[test_id:737][posneg:positive]should match memory with overcommit enabled", func() {
				vmi.Spec.Domain.Resources = v1.ResourceRequirements{
					Requests: kubev1.ResourceList{
						kubev1.ResourceMemory: resource.MustParse("100M"),
					},
					OvercommitGuestOverhead: true,
				}
				vmi = tests.RunVMIAndExpectLaunch(vmi, 30)

				readyPod := tests.GetPodByVirtualMachineInstance(vmi, tests.NamespaceTestDefault)
				computeContainer := tests.GetComputeContainerOfPod(readyPod)

				Expect(computeContainer.Resources.Requests.Memory().String()).To(Equal("100M"))
			})

			It("[test_id:2444][posneg:negative]should not match memory with overcommit disabled", func() {
				vmi.Spec.Domain.Resources = v1.ResourceRequirements{
					Requests: kubev1.ResourceList{
						kubev1.ResourceMemory: resource.MustParse("100M"),
					},
				}
				vmi = tests.RunVMIAndExpectLaunch(vmi, 30)

				readyPod := tests.GetPodByVirtualMachineInstance(vmi, tests.NamespaceTestDefault)
				computeContainer := tests.GetComputeContainerOfPod(readyPod)

				Expect(computeContainer.Resources.Requests.Memory().String()).ToNot(Equal("100M"))
			})

			It("[test_id:713]should have more memory on pod when headless", func() {
				normalVmi := tests.NewRandomVMIWithEphemeralDisk(cd.ContainerDiskFor(cd.ContainerDiskAlpine))

				vmi = tests.RunVMIAndExpectLaunch(vmi, 30)
				normalVmi = tests.RunVMIAndExpectLaunch(normalVmi, 30)

				readyPod := tests.GetPodByVirtualMachineInstance(vmi, tests.NamespaceTestDefault)
				computeContainer := tests.GetComputeContainerOfPod(readyPod)

				normalReadyPod := tests.GetPodByVirtualMachineInstance(normalVmi, tests.NamespaceTestDefault)
				normalComputeContainer := tests.GetComputeContainerOfPod(normalReadyPod)

				memDiff := normalComputeContainer.Resources.Requests.Memory()
				memDiff.Sub(*computeContainer.Resources.Requests.Memory())

				Expect(memDiff.ScaledValue(resource.Mega) > 15).To(BeTrue(), "memory difference between headless and normal should be roughly 16M")
			})

			It("[test_id:738][posneg:negative]should not connect to VNC", func() {
				vmi = tests.RunVMIAndExpectLaunch(vmi, 30)

				_, err := virtClient.VirtualMachineInstance(vmi.ObjectMeta.Namespace).VNC(vmi.ObjectMeta.Name)

				Expect(err.Error()).To(Equal("No graphics devices are present."), "vnc should not connect on headless VM")
			})

			It("[test_id:709][posneg:positive]should connect to console", func() {
				vmi = tests.RunVMIAndExpectLaunch(vmi, 30)

				By("checking that console works")
				expecter, err := tests.LoggedInAlpineExpecter(vmi)
				Expect(err).ToNot(HaveOccurred())
				defer expecter.Close()
			})

		})

		Context("without headless", func() {

			It("[test_id:714][posneg:negative]should have one vnc graphic device in xml", func() {
				tests.RunVMIAndExpectLaunch(vmi, 30)

				runningVMISpec, err := tests.GetRunningVMIDomainSpec(vmi)
				Expect(err).ToNot(HaveOccurred(), "should get vmi spec without problem")

				vncCount := 0
				for _, gr := range runningVMISpec.Devices.Graphics {
					if strings.ToLower(gr.Type) == "vnc" {
						vncCount += 1
					}
				}
				Expect(vncCount).To(Equal(1), "should have exactly one VNC device")
			})

		})

	})

})
<|endoftext|>