<commit_before>package graval

import (
	"bufio"
	"bytes"
	"crypto/rand"
	"crypto/sha256"
	"crypto/tls"
	"encoding/hex"
	"fmt"
	"io"
	"net"
	"path/filepath"
	"strings"
	"time"
)

type ftpConn struct {
	conn          net.Conn
	controlReader *bufio.Reader
	controlWriter *bufio.Writer
	dataConn      ftpDataSocket
	driver        FTPDriver
	logger        *ftpLogger
	passiveOpts   *PassiveOpts
	cryptoConfig  *CryptoConfig
	serverName    string
	sessionId     string
	namePrefix    string
	reqUser       string
	user          string
	renameFrom    string
	usingTls      bool
	usingPbsz     bool
	usingProt     bool
}

// NewftpConn constructs a new object that will handle the FTP protocol over
// an active net.TCPConn. The TCP connection should already be open before
// it is handed to this functions. driver is an instance of FTPDriver that
// will handle all auth and persistence details.
func newftpConn(tcpConn *net.TCPConn, driver FTPDriver, serverName string, passiveOpts *PassiveOpts, cryptoConfig *CryptoConfig) *ftpConn {
	c := new(ftpConn)
	c.namePrefix = "/"
	c.conn = tcpConn
	c.driver = driver
	c.sessionId = newSessionId()
	c.logger = newFtpLogger(c.sessionId)
	c.passiveOpts = passiveOpts
	c.cryptoConfig = cryptoConfig
	c.serverName = serverName

	c.usingTls = false
	c.usingPbsz = false
	c.usingProt = false

	if cryptoConfig.Implicit {
		c.startTls()
	} else {
		c.setupReaderWriter()
	}

	return c
}

// returns a random 20 char string that can be used as a unique session ID
func newSessionId() string {
	hash := sha256.New()
	_, err := io.CopyN(hash, rand.Reader, 50)
	if err != nil {
		return "????????????????????"
	}
	md := hash.Sum(nil)
	mdStr := hex.EncodeToString(md)
	return mdStr[0:20]
}

func (ftpConn *ftpConn) setupReaderWriter() {
	ftpConn.controlReader = bufio.NewReader(ftpConn.conn)
	ftpConn.controlWriter = bufio.NewWriter(ftpConn.conn)
}

// Serve starts an endless loop that reads FTP commands from the client and
// responds appropriately. terminated is a channel that will receive a true
// message when the connection closes. This loop will be running inside a
// goroutine, so use this channel to be notified when the connection can be
// cleaned up.
func (ftpConn *ftpConn) Serve() {
	defer func() {
		if r := recover(); r != nil {
			ftpConn.logger.Printf("Recovered in ftpConn Serve: %s", r)
		}

		ftpConn.Close()
	}()

	ftpConn.logger.Print("Connection Established")
	// send welcome
	ftpConn.writeMessage(220, ftpConn.serverName)
	// read commands
	for {
		line, err := ftpConn.controlReader.ReadString('\n')
		if err != nil {
			break
		}
		ftpConn.receiveLine(line)
	}
	ftpConn.logger.Print("Connection Terminated")
}

// Close will manually close this connection, even if the client isn't ready.
func (ftpConn *ftpConn) Close() {
	ftpConn.conn.Close()
	if ftpConn.dataConn != nil {
		ftpConn.dataConn.Close()
	}
}

// receiveLine accepts a single line FTP command and co-ordinates an
// appropriate response.
func (ftpConn *ftpConn) receiveLine(line string) {
	command, param := ftpConn.parseLine(line)
	ftpConn.logger.PrintCommand(command, param)
	cmdObj := commands[command]
	if cmdObj == nil {
		ftpConn.writeMessage(500, "Command not found")
		return
	}
	if cmdObj.RequireParam() && param == "" {
		ftpConn.writeMessage(553, "action aborted, required param missing")
	} else if cmdObj.RequireAuth() && ftpConn.user == "" {
		ftpConn.writeMessage(530, "not logged in")
	} else {
		cmdObj.Execute(ftpConn, param)
	}
}

func (ftpConn *ftpConn) parseLine(line string) (string, string) {
	params := strings.SplitN(strings.Trim(line, "\r\n"), " ", 2)
	if len(params) == 1 {
		return params[0], ""
	}
	return params[0], strings.TrimSpace(params[1])
}

// writeMessage will send a standard FTP response back to the client.
func (ftpConn *ftpConn) writeMessage(code int, message string) (wrote int, err error) {
	ftpConn.logger.PrintResponse(code, message)
	line := fmt.Sprintf("%d %s\r\n", code, message)
	wrote, err = ftpConn.controlWriter.WriteString(line)
	ftpConn.controlWriter.Flush()
	return
}

// writeLines will send a multiline FTP response back to the client.
func (ftpConn *ftpConn) writeLines(code int, lines ...string) (wrote int, err error) {
	message := strings.Join(lines, "\r\n") + "\r\n"
	ftpConn.logger.PrintResponse(code, message)
	wrote, err = ftpConn.controlWriter.WriteString(message)
	ftpConn.controlWriter.Flush()
	return
}

// buildPath takes a client supplied path or filename and generates a safe
// absolute path within their account sandbox.
//
//    buildpath("/")
//    => "/"
//    buildpath("one.txt")
//    => "/one.txt"
//    buildpath("/files/two.txt")
//    => "/files/two.txt"
//    buildpath("files/two.txt")
//    => "files/two.txt"
//    buildpath("/../../../../etc/passwd")
//    => "/etc/passwd"
//
// The driver implementation is responsible for deciding how to treat this path.
// Obviously they MUST NOT just read the path off disk. The probably want to
// prefix the path with something to scope the users access to a sandbox.
func (ftpConn *ftpConn) buildPath(filename string) (fullPath string) {
	if len(filename) > 0 && filename[0:1] == "/" {
		fullPath = filepath.Clean(filename)
	} else if len(filename) > 0 && filename != "-a" {
		fullPath = filepath.Clean(ftpConn.namePrefix + "/" + filename)
	} else {
		fullPath = filepath.Clean(ftpConn.namePrefix)
	}
	fullPath = strings.Replace(fullPath, "//", "/", -1)
	return
}

// sendOutofbandData will copy data from reader to the client via the currently
// open data socket. Assumes the socket is open and ready to be used.
func (ftpConn *ftpConn) sendOutofbandReader(reader io.Reader) {
	defer ftpConn.dataConn.Close()

	// we need an empty write for TLS connection if reader is empty
	_, _ = ftpConn.dataConn.Write([]byte{})

	_, err := io.Copy(ftpConn.dataConn, reader)

	if err != nil {
		ftpConn.logger.Printf("sendOutofbandReader copy error %s", err)
		ftpConn.writeMessage(550, "Action not taken")
		return
	}

	ftpConn.writeMessage(226, "Transfer complete.")

	// Chrome dies on localhost if we close connection to soon
	time.Sleep(10 * time.Millisecond)
}

// sendOutofbandData will send a string to the client via the currently open
// data socket. Assumes the socket is open and ready to be used.
func (ftpConn *ftpConn) sendOutofbandData(data string) {
	ftpConn.sendOutofbandReader(bytes.NewReader([]byte(data)))
}

func (ftpConn *ftpConn) canStartTls() bool {
	return ftpConn.cryptoConfig != nil && ftpConn.cryptoConfig.TlsConfig != nil
}

func (ftpConn *ftpConn) startTls() {
	if ftpConn.usingTls {
		return
	}

	ftpConn.conn = tls.Server(ftpConn.conn, ftpConn.cryptoConfig.TlsConfig)
	ftpConn.setupReaderWriter()

	ftpConn.usingTls = true

	return
}

func (ftpConn *ftpConn) newPassiveSocket() (socket *ftpPassiveSocket, err error) {
	if ftpConn.dataConn != nil {
		ftpConn.dataConn.Close()
		ftpConn.dataConn = nil
	}

	var tlsConfig *tls.Config

	if ftpConn.usingProt {
		tlsConfig = ftpConn.cryptoConfig.TlsConfig
	}

	socket, err = newPassiveSocket(ftpConn.logger, ftpConn.passiveOpts, tlsConfig)

	if err == nil {
		ftpConn.dataConn = socket
	}

	return
}

func (ftpConn *ftpConn) newActiveSocket(host string, port int) (socket *ftpActiveSocket, err error) {
	if ftpConn.dataConn != nil {
		ftpConn.dataConn.Close()
		ftpConn.dataConn = nil
	}

	socket, err = newActiveSocket(host, port, ftpConn.logger)

	if err == nil {
		ftpConn.dataConn = socket
	}

	return
}
<commit_msg>Handle LIST flags<commit_after>package graval

import (
	"bufio"
	"bytes"
	"crypto/rand"
	"crypto/sha256"
	"crypto/tls"
	"encoding/hex"
	"fmt"
	"io"
	"net"
	"path/filepath"
	"strings"
	"time"
)

type ftpConn struct {
	conn          net.Conn
	controlReader *bufio.Reader
	controlWriter *bufio.Writer
	dataConn      ftpDataSocket
	driver        FTPDriver
	logger        *ftpLogger
	passiveOpts   *PassiveOpts
	cryptoConfig  *CryptoConfig
	serverName    string
	sessionId     string
	namePrefix    string
	reqUser       string
	user          string
	renameFrom    string
	usingTls      bool
	usingPbsz     bool
	usingProt     bool
}

// NewftpConn constructs a new object that will handle the FTP protocol over
// an active net.TCPConn. The TCP connection should already be open before
// it is handed to this functions. driver is an instance of FTPDriver that
// will handle all auth and persistence details.
func newftpConn(tcpConn *net.TCPConn, driver FTPDriver, serverName string, passiveOpts *PassiveOpts, cryptoConfig *CryptoConfig) *ftpConn {
	c := new(ftpConn)
	c.namePrefix = "/"
	c.conn = tcpConn
	c.driver = driver
	c.sessionId = newSessionId()
	c.logger = newFtpLogger(c.sessionId)
	c.passiveOpts = passiveOpts
	c.cryptoConfig = cryptoConfig
	c.serverName = serverName

	c.usingTls = false
	c.usingPbsz = false
	c.usingProt = false

	if cryptoConfig.Implicit {
		c.startTls()
	} else {
		c.setupReaderWriter()
	}

	return c
}

// returns a random 20 char string that can be used as a unique session ID
func newSessionId() string {
	hash := sha256.New()
	_, err := io.CopyN(hash, rand.Reader, 50)
	if err != nil {
		return "????????????????????"
	}
	md := hash.Sum(nil)
	mdStr := hex.EncodeToString(md)
	return mdStr[0:20]
}

func (ftpConn *ftpConn) setupReaderWriter() {
	ftpConn.controlReader = bufio.NewReader(ftpConn.conn)
	ftpConn.controlWriter = bufio.NewWriter(ftpConn.conn)
}

// Serve starts an endless loop that reads FTP commands from the client and
// responds appropriately. terminated is a channel that will receive a true
// message when the connection closes. This loop will be running inside a
// goroutine, so use this channel to be notified when the connection can be
// cleaned up.
func (ftpConn *ftpConn) Serve() {
	defer func() {
		if r := recover(); r != nil {
			ftpConn.logger.Printf("Recovered in ftpConn Serve: %s", r)
		}

		ftpConn.Close()
	}()

	ftpConn.logger.Print("Connection Established")
	// send welcome
	ftpConn.writeMessage(220, ftpConn.serverName)
	// read commands
	for {
		line, err := ftpConn.controlReader.ReadString('\n')
		if err != nil {
			break
		}
		ftpConn.receiveLine(line)
	}
	ftpConn.logger.Print("Connection Terminated")
}

// Close will manually close this connection, even if the client isn't ready.
func (ftpConn *ftpConn) Close() {
	ftpConn.conn.Close()
	if ftpConn.dataConn != nil {
		ftpConn.dataConn.Close()
	}
}

// receiveLine accepts a single line FTP command and co-ordinates an
// appropriate response.
func (ftpConn *ftpConn) receiveLine(line string) {
	command, param := ftpConn.parseLine(line)
	ftpConn.logger.PrintCommand(command, param)
	cmdObj := commands[command]
	if cmdObj == nil {
		ftpConn.writeMessage(500, "Command not found")
		return
	}
	if cmdObj.RequireParam() && param == "" {
		ftpConn.writeMessage(553, "action aborted, required param missing")
	} else if cmdObj.RequireAuth() && ftpConn.user == "" {
		ftpConn.writeMessage(530, "not logged in")
	} else {
		cmdObj.Execute(ftpConn, param)
	}
}

func (ftpConn *ftpConn) parseLine(line string) (string, string) {
	params := strings.SplitN(strings.Trim(line, "\r\n"), " ", 2)
	if len(params) == 1 {
		return params[0], ""
	}
	return params[0], strings.TrimSpace(params[1])
}

// writeMessage will send a standard FTP response back to the client.
func (ftpConn *ftpConn) writeMessage(code int, message string) (wrote int, err error) {
	ftpConn.logger.PrintResponse(code, message)
	line := fmt.Sprintf("%d %s\r\n", code, message)
	wrote, err = ftpConn.controlWriter.WriteString(line)
	ftpConn.controlWriter.Flush()
	return
}

// writeLines will send a multiline FTP response back to the client.
func (ftpConn *ftpConn) writeLines(code int, lines ...string) (wrote int, err error) {
	message := strings.Join(lines, "\r\n") + "\r\n"
	ftpConn.logger.PrintResponse(code, message)
	wrote, err = ftpConn.controlWriter.WriteString(message)
	ftpConn.controlWriter.Flush()
	return
}

// buildPath takes a client supplied path or filename and generates a safe
// absolute path within their account sandbox.
//
//    buildpath("/")
//    => "/"
//    buildpath("one.txt")
//    => "/one.txt"
//    buildpath("/files/two.txt")
//    => "/files/two.txt"
//    buildpath("files/two.txt")
//    => "files/two.txt"
//    buildpath("/../../../../etc/passwd")
//    => "/etc/passwd"
//
// The driver implementation is responsible for deciding how to treat this path.
// Obviously they MUST NOT just read the path off disk. The probably want to
// prefix the path with something to scope the users access to a sandbox.
func (ftpConn *ftpConn) buildPath(filename string) (fullPath string) {
	low := strings.ToLower(filename)

	if len(filename) > 0 && filename[0:1] == "/" {
		fullPath = filepath.Clean(filename)
	} else if len(filename) > 0 && low != "-a" && low != "-l" && low != "-al" && low != "-la" {
		fullPath = filepath.Clean(ftpConn.namePrefix + "/" + filename)
	} else {
		fullPath = filepath.Clean(ftpConn.namePrefix)
	}
	fullPath = strings.Replace(fullPath, "//", "/", -1)
	return
}

// sendOutofbandData will copy data from reader to the client via the currently
// open data socket. Assumes the socket is open and ready to be used.
func (ftpConn *ftpConn) sendOutofbandReader(reader io.Reader) {
	defer ftpConn.dataConn.Close()

	// we need an empty write for TLS connection if reader is empty
	_, _ = ftpConn.dataConn.Write([]byte{})

	_, err := io.Copy(ftpConn.dataConn, reader)

	if err != nil {
		ftpConn.logger.Printf("sendOutofbandReader copy error %s", err)
		ftpConn.writeMessage(550, "Action not taken")
		return
	}

	ftpConn.writeMessage(226, "Transfer complete.")

	// Chrome dies on localhost if we close connection to soon
	time.Sleep(10 * time.Millisecond)
}

// sendOutofbandData will send a string to the client via the currently open
// data socket. Assumes the socket is open and ready to be used.
func (ftpConn *ftpConn) sendOutofbandData(data string) {
	ftpConn.sendOutofbandReader(bytes.NewReader([]byte(data)))
}

func (ftpConn *ftpConn) canStartTls() bool {
	return ftpConn.cryptoConfig != nil && ftpConn.cryptoConfig.TlsConfig != nil
}

func (ftpConn *ftpConn) startTls() {
	if ftpConn.usingTls {
		return
	}

	ftpConn.conn = tls.Server(ftpConn.conn, ftpConn.cryptoConfig.TlsConfig)
	ftpConn.setupReaderWriter()

	ftpConn.usingTls = true

	return
}

func (ftpConn *ftpConn) newPassiveSocket() (socket *ftpPassiveSocket, err error) {
	if ftpConn.dataConn != nil {
		ftpConn.dataConn.Close()
		ftpConn.dataConn = nil
	}

	var tlsConfig *tls.Config

	if ftpConn.usingProt {
		tlsConfig = ftpConn.cryptoConfig.TlsConfig
	}

	socket, err = newPassiveSocket(ftpConn.logger, ftpConn.passiveOpts, tlsConfig)

	if err == nil {
		ftpConn.dataConn = socket
	}

	return
}

func (ftpConn *ftpConn) newActiveSocket(host string, port int) (socket *ftpActiveSocket, err error) {
	if ftpConn.dataConn != nil {
		ftpConn.dataConn.Close()
		ftpConn.dataConn = nil
	}

	socket, err = newActiveSocket(host, port, ftpConn.logger)

	if err == nil {
		ftpConn.dataConn = socket
	}

	return
}
<|endoftext|>


<commit_before>package tele

import (
	"context"
	"time"

	"github.com/golang/protobuf/proto"
	"github.com/juju/errors"
	"github.com/temoto/spq"
	tele_api "github.com/temoto/vender/head/tele/api"
	tele_config "github.com/temoto/vender/head/tele/config"
	"github.com/temoto/vender/helpers"
	"github.com/temoto/vender/log2"
)

const (
	defaultStateInterval  = 5 * time.Minute
	defaultNetworkTimeout = 30 * time.Second
)

// Tele contract:
// - Init() fails only with invalid config, network issues ignored
// - Transaction/Error/Service/etc public API calls block at most for disk write
//   network may be slow or absent, messages will be delivered in background
// - Close() will block until all messages are delivered
// - Telemetry/Response messages delivered at least once
// - Status messages may be lost
type Tele struct { //nolint:maligned
	enabled       bool
	log           *log2.Log
	transport     Transporter
	q             *spq.Queue
	stateCh       chan tele_api.State
	stopCh        chan struct{}
	vmId          int32
	stateInterval time.Duration
	stat          tele_api.Stat
}

func (self *Tele) Init(ctx context.Context, log *log2.Log, teleConfig tele_config.Config) error {
	self.enabled = teleConfig.Enabled
	self.log = log.Clone(log2.LInfo)
	if teleConfig.LogDebug {
		self.log.SetLevel(log2.LDebug)
	}
	if !self.enabled {
		return nil
	}

	self.stopCh = make(chan struct{})
	self.stateCh = make(chan tele_api.State)
	self.vmId = int32(teleConfig.VmId)
	self.stateInterval = helpers.IntSecondDefault(teleConfig.StateIntervalSec, defaultStateInterval)
	self.stat.Locked_Reset()

	if teleConfig.PersistPath == "" {
		panic("code error must set teleConfig.PersistPath")
	}
	var err error
	self.q, err = spq.Open(teleConfig.PersistPath)
	if err != nil {
		return errors.Annotate(err, "tele queue")
	}

	willPayload := []byte{byte(tele_api.State_Disconnected)}
	// test code sets .transport
	if self.transport == nil { // production path
		self.transport = &transportMqtt{}
	}
	if err := self.transport.Init(ctx, log, teleConfig, self.onCommandMessage, willPayload); err != nil {
		return errors.Annotate(err, "tele transport")
	}

	go self.qworker()
	go self.stateWorker()
	self.stateCh <- tele_api.State_Boot
	return nil
}

func (self *Tele) Close() {
	close(self.stopCh)
	self.q.Close()
}

func (self *Tele) stateWorker() {
	const retryInterval = 17 * time.Second
	var b [1]byte
	var sent bool
	tmrRegular := time.NewTicker(self.stateInterval)
	tmrRetry := time.NewTicker(retryInterval)
	for {
		select {
		case next := <-self.stateCh:
			if next != tele_api.State(b[0]) {
				b[0] = byte(next)
				sent = self.transport.SendState(b[:])
			}

		case <-tmrRegular.C:
			sent = self.transport.SendState(b[:])

		case <-tmrRetry.C:
			if !sent {
				sent = self.transport.SendState(b[:])
			}

		case <-self.stopCh:
			return
		}
	}
}

// denote value type in persistent queue bytes form
const (
	qCommandResponse byte = 1
	qTelemetry       byte = 2
)

func (self *Tele) qworker() {
	for {
		box, err := self.q.Peek()
		switch err {
		case nil:
			// success path
			b := box.Bytes()
			// self.log.Debugf("q.peek %x", b)
			var del bool
			del, err = self.qhandle(b)
			if err != nil {
				self.log.Errorf("tele qhandle b=%x err=%v", b, err)
			}
			if del {
				err = self.q.Delete(box)
				if err != nil {
					self.log.Errorf("tele qhandle Delete b=%x err=%v", b, err)
				}
			} else {
				// FIXME delete+re-push atomically inside spq
				self.q.Delete(box)
				self.q.Push(b)
			}

		case spq.ErrClosed:
			select {
			case <-self.stopCh: // success path
			default:
				self.log.Errorf("CRITICAL tele spq closed unexpectedly")
				// TODO try to send telemetry?
			}
			return

		default:
			self.log.Errorf("CRITICAL tele spq err=%v", err)
			// here will go yet unhandled shit like disk full
		}
	}
}

func (self *Tele) qhandle(b []byte) (bool, error) {
	if len(b) == 0 {
		self.log.Errorf("tele spq peek=empty")
		// what else can we do?
		return true, nil
	}

	switch b[0] {
	case qCommandResponse:
		var r tele_api.Response
		if err := proto.Unmarshal(b[1:], &r); err != nil {
			return true, err
		}
		return self.qsendResponse(&r), nil

	case qTelemetry:
		var tm tele_api.Telemetry
		if err := proto.Unmarshal(b[1:], &tm); err != nil {
			return true, err
		}
		return self.qsendTelemetry(&tm), nil

	default:
		err := errors.Errorf("unknown kind=%d", b[0])
		return true, err
	}
}

func (self *Tele) qpushCommandResponse(c *tele_api.Command, r *tele_api.Response) error {
	if c.ReplyTopic == "" {
		err := errors.Errorf("command with reply_topic=empty")
		self.Error(err)
		return err
	}
	r.INTERNALTopic = c.ReplyTopic
	return self.qpushTagProto(qCommandResponse, r)
}

func (self *Tele) qpushTelemetry(tm *tele_api.Telemetry) error {
	if tm.VmId == 0 {
		tm.VmId = self.vmId
	}
	self.stat.Lock()
	defer self.stat.Unlock()
	tm.Stat = &self.stat.Telemetry_Stat
	err := self.qpushTagProto(qTelemetry, tm)
	self.stat.Locked_Reset()
	return err
}

func (self *Tele) qpushTagProto(tag byte, pb proto.Message) error {
	buf := proto.NewBuffer(make([]byte, 0, 1024))
	if err := buf.EncodeVarint(uint64(tag)); err != nil {
		return err
	}
	if err := buf.Marshal(pb); err != nil {
		return err
	}
	// self.log.Debugf("qpushTagProto %x", buf.Bytes())
	return self.q.Push(buf.Bytes())
}

func (self *Tele) qsendResponse(r *tele_api.Response) bool {
	// do not serialize INTERNAL_topic field
	wireResponse := *r
	wireResponse.INTERNALTopic = ""
	payload, err := proto.Marshal(&wireResponse)
	if err != nil {
		self.log.Errorf("CRITICAL response Marshal r=%#v err=%v", r, err)
		return true // retry will not help
	}
	return self.transport.SendCommandResponse(r.INTERNALTopic, payload)
}

func (self *Tele) qsendTelemetry(tm *tele_api.Telemetry) bool {
	payload, err := proto.Marshal(tm)
	if err != nil {
		self.log.Errorf("CRITICAL telemetry Marshal tm=%#v err=%v", tm, err)
		return true // retry will not help
	}
	// self.log.Debugf("SendTelemetry %x", payload)
	return self.transport.SendTelemetry(payload)
}
<commit_msg>tele: fill default telemetry time<commit_after>package tele

import (
	"context"
	"time"

	"github.com/golang/protobuf/proto"
	"github.com/juju/errors"
	"github.com/temoto/spq"
	tele_api "github.com/temoto/vender/head/tele/api"
	tele_config "github.com/temoto/vender/head/tele/config"
	"github.com/temoto/vender/helpers"
	"github.com/temoto/vender/log2"
)

const (
	defaultStateInterval  = 5 * time.Minute
	defaultNetworkTimeout = 30 * time.Second
)

// Tele contract:
// - Init() fails only with invalid config, network issues ignored
// - Transaction/Error/Service/etc public API calls block at most for disk write
//   network may be slow or absent, messages will be delivered in background
// - Close() will block until all messages are delivered
// - Telemetry/Response messages delivered at least once
// - Status messages may be lost
type Tele struct { //nolint:maligned
	enabled       bool
	log           *log2.Log
	transport     Transporter
	q             *spq.Queue
	stateCh       chan tele_api.State
	stopCh        chan struct{}
	vmId          int32
	stateInterval time.Duration
	stat          tele_api.Stat
}

func (self *Tele) Init(ctx context.Context, log *log2.Log, teleConfig tele_config.Config) error {
	self.enabled = teleConfig.Enabled
	self.log = log.Clone(log2.LInfo)
	if teleConfig.LogDebug {
		self.log.SetLevel(log2.LDebug)
	}
	if !self.enabled {
		return nil
	}

	self.stopCh = make(chan struct{})
	self.stateCh = make(chan tele_api.State)
	self.vmId = int32(teleConfig.VmId)
	self.stateInterval = helpers.IntSecondDefault(teleConfig.StateIntervalSec, defaultStateInterval)
	self.stat.Locked_Reset()

	if teleConfig.PersistPath == "" {
		panic("code error must set teleConfig.PersistPath")
	}
	var err error
	self.q, err = spq.Open(teleConfig.PersistPath)
	if err != nil {
		return errors.Annotate(err, "tele queue")
	}

	willPayload := []byte{byte(tele_api.State_Disconnected)}
	// test code sets .transport
	if self.transport == nil { // production path
		self.transport = &transportMqtt{}
	}
	if err := self.transport.Init(ctx, log, teleConfig, self.onCommandMessage, willPayload); err != nil {
		return errors.Annotate(err, "tele transport")
	}

	go self.qworker()
	go self.stateWorker()
	self.stateCh <- tele_api.State_Boot
	return nil
}

func (self *Tele) Close() {
	close(self.stopCh)
	self.q.Close()
}

func (self *Tele) stateWorker() {
	const retryInterval = 17 * time.Second
	var b [1]byte
	var sent bool
	tmrRegular := time.NewTicker(self.stateInterval)
	tmrRetry := time.NewTicker(retryInterval)
	for {
		select {
		case next := <-self.stateCh:
			if next != tele_api.State(b[0]) {
				b[0] = byte(next)
				sent = self.transport.SendState(b[:])
			}

		case <-tmrRegular.C:
			sent = self.transport.SendState(b[:])

		case <-tmrRetry.C:
			if !sent {
				sent = self.transport.SendState(b[:])
			}

		case <-self.stopCh:
			return
		}
	}
}

// denote value type in persistent queue bytes form
const (
	qCommandResponse byte = 1
	qTelemetry       byte = 2
)

func (self *Tele) qworker() {
	for {
		box, err := self.q.Peek()
		switch err {
		case nil:
			// success path
			b := box.Bytes()
			// self.log.Debugf("q.peek %x", b)
			var del bool
			del, err = self.qhandle(b)
			if err != nil {
				self.log.Errorf("tele qhandle b=%x err=%v", b, err)
			}
			if del {
				err = self.q.Delete(box)
				if err != nil {
					self.log.Errorf("tele qhandle Delete b=%x err=%v", b, err)
				}
			} else {
				// FIXME delete+re-push atomically inside spq
				self.q.Delete(box)
				self.q.Push(b)
			}

		case spq.ErrClosed:
			select {
			case <-self.stopCh: // success path
			default:
				self.log.Errorf("CRITICAL tele spq closed unexpectedly")
				// TODO try to send telemetry?
			}
			return

		default:
			self.log.Errorf("CRITICAL tele spq err=%v", err)
			// here will go yet unhandled shit like disk full
		}
	}
}

func (self *Tele) qhandle(b []byte) (bool, error) {
	if len(b) == 0 {
		self.log.Errorf("tele spq peek=empty")
		// what else can we do?
		return true, nil
	}

	switch b[0] {
	case qCommandResponse:
		var r tele_api.Response
		if err := proto.Unmarshal(b[1:], &r); err != nil {
			return true, err
		}
		return self.qsendResponse(&r), nil

	case qTelemetry:
		var tm tele_api.Telemetry
		if err := proto.Unmarshal(b[1:], &tm); err != nil {
			return true, err
		}
		return self.qsendTelemetry(&tm), nil

	default:
		err := errors.Errorf("unknown kind=%d", b[0])
		return true, err
	}
}

func (self *Tele) qpushCommandResponse(c *tele_api.Command, r *tele_api.Response) error {
	if c.ReplyTopic == "" {
		err := errors.Errorf("command with reply_topic=empty")
		self.Error(err)
		return err
	}
	r.INTERNALTopic = c.ReplyTopic
	return self.qpushTagProto(qCommandResponse, r)
}

func (self *Tele) qpushTelemetry(tm *tele_api.Telemetry) error {
	if tm.VmId == 0 {
		tm.VmId = self.vmId
	}
	if tm.Time == 0 {
		tm.Time = time.Now().UnixNano()
	}
	self.stat.Lock()
	defer self.stat.Unlock()
	tm.Stat = &self.stat.Telemetry_Stat
	err := self.qpushTagProto(qTelemetry, tm)
	self.stat.Locked_Reset()
	return err
}

func (self *Tele) qpushTagProto(tag byte, pb proto.Message) error {
	buf := proto.NewBuffer(make([]byte, 0, 1024))
	if err := buf.EncodeVarint(uint64(tag)); err != nil {
		return err
	}
	if err := buf.Marshal(pb); err != nil {
		return err
	}
	// self.log.Debugf("qpushTagProto %x", buf.Bytes())
	return self.q.Push(buf.Bytes())
}

func (self *Tele) qsendResponse(r *tele_api.Response) bool {
	// do not serialize INTERNAL_topic field
	wireResponse := *r
	wireResponse.INTERNALTopic = ""
	payload, err := proto.Marshal(&wireResponse)
	if err != nil {
		self.log.Errorf("CRITICAL response Marshal r=%#v err=%v", r, err)
		return true // retry will not help
	}
	return self.transport.SendCommandResponse(r.INTERNALTopic, payload)
}

func (self *Tele) qsendTelemetry(tm *tele_api.Telemetry) bool {
	payload, err := proto.Marshal(tm)
	if err != nil {
		self.log.Errorf("CRITICAL telemetry Marshal tm=%#v err=%v", tm, err)
		return true // retry will not help
	}
	// self.log.Debugf("SendTelemetry %x", payload)
	return self.transport.SendTelemetry(payload)
}
<|endoftext|>


<commit_before>// Copyright 2013, Christian Vozar
//
// 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 hipchat

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/mozilla-services/heka/message"
	. "github.com/mozilla-services/heka/pipeline"
	"ioutil"
	"net/http"
	"net/url"
)

type HipchatOutput struct {
	conf   *HipchatOutputConfig
	url    string
	format string
}

// Hipchat Output config struct
type HipchatOutputConfig struct {
	// Outputs the payload attribute in the HipChat message vs a full JSON message dump
	PayloadOnly bool `toml:"payload_only"`
	// Url for HttpInput to GET.
	AuthToken string `toml:"auth_token"`
	// Required. ID or name of the room.
	RoomId string `toml:"room_id"`
	// Required. Name the message will appear be sent. Must be less than 15
	// characters long. May contain letters, numbers, -, _, and spaces.
	From string
	// Whether or not this message should trigger a notification for people
	// in the room (change the tab color, play a sound, etc).
	// Each recipient's notification preferences are taken into account.
	// Default is false
	Notify bool
	// Background color for message.
	// One of "yellow", "red", "green", "purple", "gray", or "random".
	// Default is gray
	Color string
}

func (ho *HipchatOutput) ConfigStruct() interface{} {
	return &HipchatOutputConfig{
		PayloadOnly: true,
		From:        "Heka",
		Notify:      false,
		Color:       "gray",
	}
}

func (ho *HipchatOutput) sendMessage(mc string) error {
	messageUri := fmt.Sprintf("%s/rooms/message?auth_token=%s", ho.url, url.QueryEscape(ho.conf.AuthToken))

	messagePayload := url.Values{
		"room_id":        {ho.conf.RoomId},
		"from":           {ho.conf.From},
		"message":        {mc},
		"color":          {ho.conf.Color},
		"message_format": {ho.format},
	}

	if ho.conf.Notify == true {
		messagePayload.Add("notify", "1")
	}

	resp, err := http.PostForm(messageUri, messagePayload)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}

	messageResponse := &struct{ Status string }{}
	if err := json.Unmarshal(body, messageResponse); err != nil {
		return err
	}
	if messageResponse.Status != "sent" {
		return errors.New("HipchatOutput: Status response was not sent.")
	}

	return nil
}

func (ho *HipchatOutput) Init(config interface{}) (err error) {
	ho.conf = config.(*HipchatOutputConfig)

	if ho.conf.RoomId == nil {
		return fmt.Errorf("room_id must contain a HipChat room ID or name")
	}

	ho.url = "https://api.hipchat.com/v1"
	ho.format = "text"
	return
}

func (ho *HipchatOutput) Run(or OutputRunner, h PluginHelper) (err error) {
	inChan := or.InChan()

	var (
		pack     *PipelinePack
		msg      *message.Message
		contents []byte
	)

	for pack = range inChan {
		msg = pack.Message
		if ho.conf.PayloadOnly {
			err = ho.sendMessage(msg.GetPayload())
		} else {
			if contents, err = json.Marshal(msg); err == nil {
				err = ho.sendMessage(string(contents))
			} else {
				or.LogError(err)
			}
		}
		if err != nil {
			or.LogError(err)
		}
		pack.Recycle()
	}
	return
}

func init() {
	RegisterPlugin("HipchatOutput", func() interface{} {
		return new(HipchatOutput)
	})
}
<commit_msg>IOUtil syntax error<commit_after>// Copyright 2013, Christian Vozar
//
// 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 hipchat

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/mozilla-services/heka/message"
	. "github.com/mozilla-services/heka/pipeline"
	"io/ioutil"
	"net/http"
	"net/url"
)

type HipchatOutput struct {
	conf   *HipchatOutputConfig
	url    string
	format string
}

// Hipchat Output config struct
type HipchatOutputConfig struct {
	// Outputs the payload attribute in the HipChat message vs a full JSON message dump
	PayloadOnly bool `toml:"payload_only"`
	// Url for HttpInput to GET.
	AuthToken string `toml:"auth_token"`
	// Required. ID or name of the room.
	RoomId string `toml:"room_id"`
	// Required. Name the message will appear be sent. Must be less than 15
	// characters long. May contain letters, numbers, -, _, and spaces.
	From string
	// Whether or not this message should trigger a notification for people
	// in the room (change the tab color, play a sound, etc).
	// Each recipient's notification preferences are taken into account.
	// Default is false
	Notify bool
	// Background color for message.
	// One of "yellow", "red", "green", "purple", "gray", or "random".
	// Default is gray
	Color string
}

func (ho *HipchatOutput) ConfigStruct() interface{} {
	return &HipchatOutputConfig{
		PayloadOnly: true,
		From:        "Heka",
		Notify:      false,
		Color:       "gray",
	}
}

func (ho *HipchatOutput) sendMessage(mc string) error {
	messageUri := fmt.Sprintf("%s/rooms/message?auth_token=%s", ho.url, url.QueryEscape(ho.conf.AuthToken))

	messagePayload := url.Values{
		"room_id":        {ho.conf.RoomId},
		"from":           {ho.conf.From},
		"message":        {mc},
		"color":          {ho.conf.Color},
		"message_format": {ho.format},
	}

	if ho.conf.Notify == true {
		messagePayload.Add("notify", "1")
	}

	resp, err := http.PostForm(messageUri, messagePayload)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}

	messageResponse := &struct{ Status string }{}
	if err := json.Unmarshal(body, messageResponse); err != nil {
		return err
	}
	if messageResponse.Status != "sent" {
		return errors.New("HipchatOutput: Status response was not sent.")
	}

	return nil
}

func (ho *HipchatOutput) Init(config interface{}) (err error) {
	ho.conf = config.(*HipchatOutputConfig)

	if ho.conf.RoomId == nil {
		return fmt.Errorf("room_id must contain a HipChat room ID or name")
	}

	ho.url = "https://api.hipchat.com/v1"
	ho.format = "text"
	return
}

func (ho *HipchatOutput) Run(or OutputRunner, h PluginHelper) (err error) {
	inChan := or.InChan()

	var (
		pack     *PipelinePack
		msg      *message.Message
		contents []byte
	)

	for pack = range inChan {
		msg = pack.Message
		if ho.conf.PayloadOnly {
			err = ho.sendMessage(msg.GetPayload())
		} else {
			if contents, err = json.Marshal(msg); err == nil {
				err = ho.sendMessage(string(contents))
			} else {
				or.LogError(err)
			}
		}
		if err != nil {
			or.LogError(err)
		}
		pack.Recycle()
	}
	return
}

func init() {
	RegisterPlugin("HipchatOutput", func() interface{} {
		return new(HipchatOutput)
	})
}
<|endoftext|>


<commit_before>package gangliamr_test

import (
	"testing"
	"time"

	"github.com/daaku/go.ganglia/gmon"
	"github.com/daaku/go.ganglia/gmondtest"
	"github.com/daaku/go.gangliamr"
	"github.com/daaku/go.metrics"
)

func TestHistogramSimple(t *testing.T) {
	t.Parallel()
	h := gmondtest.NewHarness(t)
	defer h.Stop()

	const name = "histogram_simple_metric"
	var hg metrics.Histogram
	hg = &gangliamr.Histogram{
		Histogram: metrics.NewHistogram(metrics.NewUniformSample(10)),
		Name:      name,
	}

	registry := gangliamr.Registry{
		Client:       h.Client,
		TickInterval: 5 * time.Millisecond,
	}
	registry.Register(hg)

	const v1 = 43
	hg.Update(v1)
	if hg.Count() != 1 {
		t.Fatalf("was expecting 1 got %d", hg.Count())
	}

	h.ContainsMetric(&gmon.Metric{
		Name:  name + ".count",
		Value: "1",
		Unit:  "count",
		Slope: "both",
	})

	const v2 = 42
	hg.Update(v2)
	if hg.Count() != 2 {
		t.Fatalf("was expecting 2 got %d", hg.Count())
	}

	h.ContainsMetric(&gmon.Metric{
		Name:  name + ".count",
		Value: "2",
		Unit:  "count",
		Slope: "both",
	})

	if hg.Max() != v1 {
		t.Fatalf("was expecting %d but got %d", v1, hg.Max())
	}

	h.ContainsMetric(&gmon.Metric{
		Name:  name + ".mean",
		Value: "42.5",
		Unit:  "value",
		Slope: "both",
	})
}
<commit_msg>TestHistogramMisconfigured<commit_after>package gangliamr_test

import (
	"testing"
	"time"

	"github.com/daaku/go.ganglia/gmon"
	"github.com/daaku/go.ganglia/gmondtest"
	"github.com/daaku/go.gangliamr"
	"github.com/daaku/go.metrics"
)

func TestHistogramSimple(t *testing.T) {
	t.Parallel()
	h := gmondtest.NewHarness(t)
	defer h.Stop()

	const name = "histogram_simple_metric"
	var hg metrics.Histogram
	hg = &gangliamr.Histogram{
		Histogram: metrics.NewHistogram(metrics.NewUniformSample(10)),
		Name:      name,
	}

	registry := gangliamr.Registry{
		Client:       h.Client,
		TickInterval: 5 * time.Millisecond,
	}
	registry.Register(hg)

	const v1 = 43
	hg.Update(v1)
	if hg.Count() != 1 {
		t.Fatalf("was expecting 1 got %d", hg.Count())
	}

	h.ContainsMetric(&gmon.Metric{
		Name:  name + ".count",
		Value: "1",
		Unit:  "count",
		Slope: "both",
	})

	const v2 = 42
	hg.Update(v2)
	if hg.Count() != 2 {
		t.Fatalf("was expecting 2 got %d", hg.Count())
	}

	h.ContainsMetric(&gmon.Metric{
		Name:  name + ".count",
		Value: "2",
		Unit:  "count",
		Slope: "both",
	})

	if hg.Max() != v1 {
		t.Fatalf("was expecting %d but got %d", v1, hg.Max())
	}

	h.ContainsMetric(&gmon.Metric{
		Name:  name + ".mean",
		Value: "42.5",
		Unit:  "value",
		Slope: "both",
	})
}

func TestHistogramMisconfigured(t *testing.T) {
	t.Parallel()
	defer func() {
		if r := recover(); r == nil {
			t.Fatal("was expecting a panic")
		}
	}()
	hg := &gangliamr.Histogram{}
	registry := gangliamr.Registry{
		TickInterval: 5 * time.Millisecond,
	}
	registry.Register(hg)
}
<|endoftext|>


<commit_before>package softlayer

import (
	"testing"

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

func TestAccSoftLayerLbVpxService_Basic(t *testing.T) {
	resource.Test(t, resource.TestCase{
		PreCheck:  func() { testAccPreCheck(t) },
		Providers: testAccProviders,
		Steps: []resource.TestStep{
			{
				Config: testAccCheckSoftLayerLbVpxServiceConfig_basic,
				Check: resource.ComposeTestCheckFunc(
					resource.TestCheckResourceAttr(
						"softlayer_lb_vpx_service.testacc_service1", "name", "test_load_balancer_service1"),
					resource.TestCheckResourceAttr(
						"softlayer_lb_vpx_service.testacc_service1", "destination_port", "89"),
					resource.TestCheckResourceAttr(
						"softlayer_lb_vpx_service.testacc_service1", "weight", "55"),
					resource.TestCheckResourceAttr(
						"softlayer_lb_vpx_service.testacc_service2", "name", "test_load_balancer_service2"),
					resource.TestCheckResourceAttr(
						"softlayer_lb_vpx_service.testacc_service2", "destination_port", "89"),
					resource.TestCheckResourceAttr(
						"softlayer_lb_vpx_service.testacc_service2", "weight", "55"),
				),
			},
		},
	})
}

var testAccCheckSoftLayerLbVpxServiceConfig_basic = `

resource "softlayer_virtual_guest" "vm1" {
    hostname = "vm1"
    domain = "example.com"
    os_reference_code = "DEBIAN_7_64"
    datacenter = "wdc01"
    network_speed = 10
    hourly_billing = true
    private_network_only = false
    cores = 1
    memory = 1024
    disks = [25]
    local_disk = false
}

resource "softlayer_virtual_guest" "vm2" {
    hostname = "vm2"
    domain = "example.com"
    os_reference_code = "DEBIAN_7_64"
    datacenter = "wdc01"
    network_speed = 10
    hourly_billing = true
    private_network_only = false
    cores = 1
    memory = 1024
    disks = [25]
    local_disk = false
}

resource "softlayer_lb_vpx" "testacc_vpx" {
    datacenter = "wdc01"
    speed = 10
    version = "10.1"
    plan = "Standard"
    ip_count = 2
}

resource "softlayer_lb_vpx_vip" "testacc_vip" {
    name = "test_load_balancer_vip"
    nad_controller_id = "${softlayer_lb_vpx.testacc_vpx.id}"
    load_balancing_method = "lc"
    source_port = 80
    type = "HTTP"
    virtual_ip_address = "${softlayer_lb_vpx.testacc_vpx.vip_pool[0]}"
}

resource "softlayer_lb_vpx_service" "testacc_service1" {
  name = "test_load_balancer_service1"
  vip_id = "${softlayer_lb_vpx_vip.testacc_vip.id}"
  destination_ip_address = "${softlayer_virtual_guest.vm1.ipv4_address}"
  destination_port = 89
  weight = 55
  connection_limit = 5000
  health_check = "HTTP"
}

resource "softlayer_lb_vpx_service" "testacc_service2" {
  name = "test_load_balancer_service2"
  vip_id = "${softlayer_lb_vpx_vip.testacc_vip.id}"
  destination_ip_address = "${softlayer_virtual_guest.vm2.ipv4_address}"
  destination_port = 89
  weight = 55
  connection_limit = 5000
  health_check = "HTTP"
}
`
<commit_msg>Add a test case for vpx 10.5 service.<commit_after>package softlayer

import (
	"testing"

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

func TestAccSoftLayerLbVpxService_Basic(t *testing.T) {
	resource.Test(t, resource.TestCase{
		PreCheck:  func() { testAccPreCheck(t) },
		Providers: testAccProviders,
		Steps: []resource.TestStep{
			{
				Config: testAccCheckSoftLayerLbVpxServiceConfig_basic,
				Check: resource.ComposeTestCheckFunc(
					resource.TestCheckResourceAttr(
						"softlayer_lb_vpx_service.testacc_service1", "name", "test_load_balancer_service1"),
					resource.TestCheckResourceAttr(
						"softlayer_lb_vpx_service.testacc_service1", "destination_port", "89"),
					resource.TestCheckResourceAttr(
						"softlayer_lb_vpx_service.testacc_service1", "weight", "55"),
					resource.TestCheckResourceAttr(
						"softlayer_lb_vpx_service.testacc_service2", "name", "test_load_balancer_service2"),
					resource.TestCheckResourceAttr(
						"softlayer_lb_vpx_service.testacc_service2", "destination_port", "89"),
					resource.TestCheckResourceAttr(
						"softlayer_lb_vpx_service.testacc_service2", "weight", "55"),
				),
			},
		},
	})
}

func TestAccSoftLayerLbVpxService_Basic105(t *testing.T) {
	resource.Test(t, resource.TestCase{
		PreCheck:  func() { testAccPreCheck(t) },
		Providers: testAccProviders,
		Steps: []resource.TestStep{
			{
				Config: testAccCheckSoftLayerLbVpxServiceConfig_basic105,
				Check: resource.ComposeTestCheckFunc(
					resource.TestCheckResourceAttr(
						"softlayer_lb_vpx_service.testacc_service3", "name", "testacc_service3"),
					resource.TestCheckResourceAttr(
						"softlayer_lb_vpx_service.testacc_service3", "destination_port", "89"),
					resource.TestCheckResourceAttr(
						"softlayer_lb_vpx_service.testacc_service4", "name", "testacc_service4"),
					resource.TestCheckResourceAttr(
						"softlayer_lb_vpx_service.testacc_service4", "destination_port", "89"),
				),
			},
		},
	})
}

var testAccCheckSoftLayerLbVpxServiceConfig_basic = `

resource "softlayer_virtual_guest" "vm1" {
    hostname = "vm1"
    domain = "example.com"
    os_reference_code = "DEBIAN_7_64"
    datacenter = "wdc01"
    network_speed = 10
    hourly_billing = true
    private_network_only = false
    cores = 1
    memory = 1024
    disks = [25]
    local_disk = false
}

resource "softlayer_virtual_guest" "vm2" {
    hostname = "vm2"
    domain = "example.com"
    os_reference_code = "DEBIAN_7_64"
    datacenter = "wdc01"
    network_speed = 10
    hourly_billing = true
    private_network_only = false
    cores = 1
    memory = 1024
    disks = [25]
    local_disk = false
}

resource "softlayer_lb_vpx" "testacc_vpx" {
    datacenter = "wdc01"
    speed = 10
    version = "10.1"
    plan = "Standard"
    ip_count = 2
}

resource "softlayer_lb_vpx_vip" "testacc_vip" {
    name = "test_load_balancer_vip"
    nad_controller_id = "${softlayer_lb_vpx.testacc_vpx.id}"
    load_balancing_method = "lc"
    source_port = 80
    type = "HTTP"
    virtual_ip_address = "${softlayer_lb_vpx.testacc_vpx.vip_pool[0]}"
}

resource "softlayer_lb_vpx_service" "testacc_service1" {
  name = "test_load_balancer_service1"
  vip_id = "${softlayer_lb_vpx_vip.testacc_vip.id}"
  destination_ip_address = "${softlayer_virtual_guest.vm1.ipv4_address}"
  destination_port = 89
  weight = 55
  connection_limit = 5000
  health_check = "HTTP"
}

resource "softlayer_lb_vpx_service" "testacc_service2" {
  name = "test_load_balancer_service2"
  vip_id = "${softlayer_lb_vpx_vip.testacc_vip.id}"
  destination_ip_address = "${softlayer_virtual_guest.vm2.ipv4_address}"
  destination_port = 89
  weight = 55
  connection_limit = 5000
  health_check = "HTTP"
}
`

var testAccCheckSoftLayerLbVpxServiceConfig_basic105 = `

resource "softlayer_virtual_guest" "vm3" {
    hostname = "vm3"
    domain = "example.com"
    os_reference_code = "DEBIAN_7_64"
    datacenter = "lon02"
    network_speed = 10
    hourly_billing = true
    private_network_only = false
    cores = 1
    memory = 1024
    disks = [25]
    local_disk = false
}

resource "softlayer_virtual_guest" "vm4" {
    hostname = "vm4"
    domain = "example.com"
    os_reference_code = "DEBIAN_7_64"
    datacenter = "lon02"
    network_speed = 10
    hourly_billing = true
    private_network_only = false
    cores = 1
    memory = 1024
    disks = [25]
    local_disk = false
}

resource "softlayer_lb_vpx" "testacc_vpx2" {
    datacenter = "lon02"
    speed = 10
    version = "10.5"
    plan = "Standard"
    ip_count = 2
}

resource "softlayer_lb_vpx_vip" "testacc_vip2" {
    name = "testacc_vip2"
    nad_controller_id = "${softlayer_lb_vpx.testacc_vpx2.id}"
    load_balancing_method = "lc"
    source_port = 80
    type = "HTTP"
    virtual_ip_address = "${softlayer_lb_vpx.testacc_vpx2.vip_pool[0]}"
}

resource "softlayer_lb_vpx_service" "testacc_service3" {
  name = "testacc_service3"
  vip_id = "${softlayer_lb_vpx_vip.testacc_vip2.id}"
  destination_ip_address = "${softlayer_virtual_guest.vm3.ipv4_address}"
  destination_port = 89
  weight = 55
  connection_limit = 5000
  health_check = "HTTP"
}

resource "softlayer_lb_vpx_service" "testacc_service4" {
  name = "testacc_service4"
  vip_id = "${softlayer_lb_vpx_vip.testacc_vip2.id}"
  destination_ip_address = "${softlayer_virtual_guest.vm4.ipv4_address}"
  destination_port = 89
  weight = 55
  connection_limit = 5000
  health_check = "HTTP"
}
`

/*
var testAccCheckSoftLayerLbVpxServiceConfig_basic105 = `

resource "softlayer_lb_vpx_vip" "testacc_vip2" {
    name = "testacc_vip2"
    nad_controller_id = 21641
    load_balancing_method = "lc"
    source_port = 80
    type = "HTTP"
    virtual_ip_address = "169.54.227.96"
}

resource "softlayer_lb_vpx_service" "testacc_service3" {
  name = "testacc_service3"
  vip_id = "${softlayer_lb_vpx_vip.testacc_vip2.id}"
  destination_ip_address = "10.10.10.10"
  destination_port = 89
  weight = 55
  connection_limit = 5000
  health_check = "HTTP"
}

resource "softlayer_lb_vpx_service" "testacc_service4" {
  name = "testacc_service4"
  vip_id = "${softlayer_lb_vpx_vip.testacc_vip2.id}"
  destination_ip_address = "20.20.20.20"
  destination_port = 89
  weight = 55
  connection_limit = 5000
  health_check = "HTTP"
}
`
*/
<|endoftext|>


<commit_before>// Copyright 2018 Istio 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 main

import (
	"net/http"
	"time"

	"github.com/spf13/cobra"

	"istio.io/istio/pilot/pkg/request"
	"istio.io/istio/pkg/cmd"
)

var (
	requestCmd = &cobra.Command{
		Use:   "request <method> <path> [<body>]",
		Short: "Makes an HTTP request to the Envoy admin API",
		Args:  cobra.MinimumNArgs(2),
		RunE: func(c *cobra.Command, args []string) error {
			cmd.PrintFlags(c.Flags())
			command := &request.Command{
				Address: "127.0.0.1:15000",
				Client: &http.Client{
					Timeout: 60 * time.Second,
				},
			}
			body := ""
			if len(args) >= 3 {
				body = args[2]
			}
			return command.Do(args[0], args[1], body)
		},
	}
)

func init() {
	rootCmd.AddCommand(requestCmd)
}
<commit_msg>Fix breakage in istioctl proxy-config (#8768)<commit_after>// Copyright 2018 Istio 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 main

import (
	"net/http"
	"time"

	"github.com/spf13/cobra"

	"istio.io/istio/pilot/pkg/request"
)

// NB: extra standard output in addition to what's returned from envoy
// must not be added in this command. Otherwise, it'd break istioctl proxy-config,
// which interprets the output literally as json document.
var (
	requestCmd = &cobra.Command{
		Use:   "request <method> <path> [<body>]",
		Short: "Makes an HTTP request to the Envoy admin API",
		Args:  cobra.MinimumNArgs(2),
		RunE: func(c *cobra.Command, args []string) error {
			command := &request.Command{
				Address: "127.0.0.1:15000",
				Client: &http.Client{
					Timeout: 60 * time.Second,
				},
			}
			body := ""
			if len(args) >= 3 {
				body = args[2]
			}
			return command.Do(args[0], args[1], body)
		},
	}
)

func init() {
	rootCmd.AddCommand(requestCmd)
}
<|endoftext|>


<commit_before>package archive

import (
	"io/ioutil"
	"os"
	"path/filepath"
	"testing"

	"github.com/goreleaser/goreleaser/config"
	"github.com/goreleaser/goreleaser/context"
	"github.com/stretchr/testify/assert"
)

func TestDescription(t *testing.T) {
	assert.NotEmpty(t, Pipe{}.Description())
}

func TestRunPipe(t *testing.T) {
	var assert = assert.New(t)
	folder, err := ioutil.TempDir("", "archivetest")
	assert.NoError(err)
	current, err := os.Getwd()
	assert.NoError(err)
	assert.NoError(os.Chdir(folder))
	defer func() {
		assert.NoError(os.Chdir(current))
	}()
	var dist = filepath.Join(folder, "dist")
	assert.NoError(os.Mkdir(dist, 0755))
	assert.NoError(os.Mkdir(filepath.Join(dist, "mybin"), 0755))
	_, err = os.Create(filepath.Join(dist, "mybin", "mybin"))
	assert.NoError(err)
	readme, err := os.Create(filepath.Join(folder, "README.md"))
	assert.NoError(err)
	var ctx = &context.Context{
		Archives: map[string]string{
			"darwinamd64":  "mybin",
			"windowsamd64": "mybin",
		},
		Config: config.Project{
			Dist: dist,
			Archive: config.Archive{
				Files: []string{
					"README.md",
				},
				FormatOverrides: []config.FormatOverride{
					{
						Goos:   "windows",
						Format: "zip",
					},
				},
			},
		},
	}
	for _, format := range []string{"tar.gz", "zip"} {
		t.Run("Archive format "+format, func(t *testing.T) {
			ctx.Config.Archive.Format = format
			assert.NoError(Pipe{}.Run(ctx))
		})
	}
	t.Run("Removed README", func(t *testing.T) {
		assert.NoError(os.Remove(readme.Name()))
		assert.Error(Pipe{}.Run(ctx))
	})
}
<commit_msg>added more tests<commit_after>package archive

import (
	"io/ioutil"
	"os"
	"path/filepath"
	"testing"

	"github.com/goreleaser/goreleaser/config"
	"github.com/goreleaser/goreleaser/context"
	"github.com/stretchr/testify/assert"
)

func TestDescription(t *testing.T) {
	assert.NotEmpty(t, Pipe{}.Description())
}

func TestRunPipe(t *testing.T) {
	var assert = assert.New(t)
	folder, err := ioutil.TempDir("", "archivetest")
	assert.NoError(err)
	current, err := os.Getwd()
	assert.NoError(err)
	assert.NoError(os.Chdir(folder))
	defer func() {
		assert.NoError(os.Chdir(current))
	}()
	var dist = filepath.Join(folder, "dist")
	assert.NoError(os.Mkdir(dist, 0755))
	assert.NoError(os.Mkdir(filepath.Join(dist, "mybin"), 0755))
	_, err = os.Create(filepath.Join(dist, "mybin", "mybin"))
	assert.NoError(err)
	readme, err := os.Create(filepath.Join(folder, "README.md"))
	assert.NoError(err)
	var ctx = &context.Context{
		Archives: map[string]string{
			"darwinamd64":  "mybin",
			"windowsamd64": "mybin",
		},
		Config: config.Project{
			Dist: dist,
			Archive: config.Archive{
				Files: []string{
					"README.md",
				},
				FormatOverrides: []config.FormatOverride{
					{
						Goos:   "windows",
						Format: "zip",
					},
				},
			},
		},
	}
	for _, format := range []string{"tar.gz", "zip"} {
		t.Run("Archive format "+format, func(t *testing.T) {
			ctx.Config.Archive.Format = format
			assert.NoError(Pipe{}.Run(ctx))
		})
	}
	t.Run("Removed README", func(t *testing.T) {
		assert.NoError(os.Remove(readme.Name()))
		assert.Error(Pipe{}.Run(ctx))
	})
}

func TestFormatFor(t *testing.T) {
	var assert = assert.New(t)
	var ctx = &context.Context{
		Config: config.Project{
			Archive: config.Archive{
				Format: "tar.gz",
				FormatOverrides: []config.FormatOverride{
					{
						Goos:   "windows",
						Format: "zip",
					},
				},
			},
		},
	}
	assert.Equal("zip", formatFor(ctx, "windowsamd64"))
	assert.Equal("tar.gz", formatFor(ctx, "linux386"))
}
<|endoftext|>


<commit_before>/*
Copyright 2018 The Knative 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 autoscaling

import "time"

const (
	// The internal autoscaling group name. This is used for CRDs.
	InternalGroupName = "autoscaling.internal.knative.dev"

	// The publuc autoscaling group name. This is used for annotations, labels, etc.
	GroupName = "autoscaling.knative.dev"

	// ClassAnnotationKey is the annotation for the explicit class of autoscaler
	// that a particular resource has opted into. For example,
	//   autoscaling.knative.dev/class: foo
	// This uses a different domain because unlike the resource, it is user-facing.
	ClassAnnotationKey = GroupName + "/class"
	// KPA is Knative Horizontal Pod Autoscaler
	KPA = "kpa.autoscaling.knative.dev"
	// HPA is Kubernetes Horizontal Pod Autoscaler
	HPA = "hpa.autoscaling.knative.dev"

	// MinScaleAnnotationKey is the annotation to specify the minimum number of Pods
	// the PodAutoscaler should provision. For example,
	//   autoscaling.knative.dev/minScale: "1"
	MinScaleAnnotationKey = GroupName + "/minScale"
	// MaxScaleAnnotationKey is the annotation to specify the maximum number of Pods
	// the PodAutoscaler should provision. For example,
	//   autoscaling.knative.dev/maxScale: "10"
	MaxScaleAnnotationKey = GroupName + "/maxScale"

	// MetricAnnotationKey is the annotation to specify what metric the PodAutoscaler
	// should be scaled on. For example,
	//   autoscaling.knative.dev/metric: cpu
	MetricAnnotationKey = GroupName + "/metric"
	// Concurrency is the number of requests in-flight at any given time.
	Concurrency = "concurrency"
	// CPU is the amount of the requested cpu actually being consumed by the Pod.
	CPU = "cpu"
	// RPS is the requests per second reaching the Pod.
	RPS = "rps"

	// TargetAnnotationKey is the annotation to specify what metric value the
	// PodAutoscaler should attempt to maintain. For example,
	//   autoscaling.knative.dev/metric: cpu
	//   autoscaling.knative.dev/target: "75"   # target 75% cpu utilization
	TargetAnnotationKey = GroupName + "/target"
	// TargetMin is the minimum allowable target. Values less than
	// zero don't make sense.
	TargetMin = 1

	// WindowAnnotationKey is the annotation to specify the time
	// interval over which to calculate the average metric.  Larger
	// values result in more smoothing. For example,
	//   autoscaling.knative.dev/metric: concurrency
	//   autoscaling.knative.dev/window: "2m"
	// Only the kpa.autoscaling.knative.dev class autoscaler supports
	// the window annotation.
	WindowAnnotationKey = GroupName + "/window"
	// WindowMin is the minimum allowable stable autoscaling
	// window. KPA-class autoscalers calculate the desired replica
	// count every 2 seconds (tick-interval in config-autoscaler) so
	// the closer the window gets to that value, the more likely data
	// points will be missed entirely by the panic window which is
	// smaller than the stable window. Anything less than 6 second
	// isn't going to work well.
	WindowMin = 6 * time.Second
	// WindowMax is the maximum permitted stable autoscaling window.
	// This keeps the event horizon to a resonable enough limit.
	WindowMax = 1 * time.Hour

	// TargetUtilizationPercentageKey is the annotation which specifies the
	// desired target resource utilization for the revision.
	// TargetUtilization is a percentage in the 1 <= TU <= 100 range.
	// This annotation takes precedence over the config map value.
	TargetUtilizationPercentageKey = GroupName + "/targetUtilizationPercentage"

	// TargetBurstCapacityKey specifies the desired burst capacity for the
	// revision. Possible values are:
	// -1 -- infinite;
	//  0 -- no TBC;
	// >0 -- actual TBC.
	// <0 && != -1 -- an error.
	TargetBurstCapacityKey = GroupName + "/targetBurstCapacity"

	// PanicWindowPercentageAnnotationKey is the annotation to
	// specify the time interval over which to calculate the average
	// metric during a spike. Where a spike is defined as the metric
	// reaching panic level within the panic window (e.g. panic
	// mode). Lower values make panic mode more sensitive. Note:
	// Panic threshold can be overridden with the
	// PanicThresholdPercentageAnnotationKey. For example,
	//   autoscaling.knative.dev/panicWindowPercentage: "5.0"
	//   autoscaling.knative.dev/panicThresholdPercentage: "150.0"
	// Only the kpa.autoscaling.knative.dev class autoscaler supports
	// the panicWindowPercentage annotation.
	// Panic window is specified as a percentag to maintain the
	// autoscaler's algorithm behavior when only the stable window is
	// specified. The panic window will change along with the stable
	// window at the default percentage.
	PanicWindowPercentageAnnotationKey = GroupName + "/panicWindowPercentage"
	// PanicWindowPercentageMin is the minimum allowable panic window
	// percentage. The autoscaler calculates desired replicas every 2
	// seconds (tick-interval in config-autoscaler), so a panic
	// window less than 2 seconds will be missing data points. One
	// percent is a very small ratio and would require a stable
	// window of at least 3.4 minutes. Anything less doesn't make
	// sense.
	PanicWindowPercentageMin = 1.0
	// PanicWindowPercentageMax is the maximum allowable panic window
	// percentage. The KPA autoscaler's panic feature allows the
	// autoscaler to be more responsive over a smaller time scale
	// when necessary. So the panic window cannot be larger than the
	// stable window.
	PanicWindowPercentageMax = 100.0

	// PanicThresholdPercentageAnnotationKey is the annotation to specify
	// the level at what level panic mode will engage when reached within
	// in the panic window. The level is defined as a percentage of
	// the metric target. Lower values make panic mode more
	// sensitive. For example,
	//   autoscaling.knative.dev/panicWindowPercentage: "5.0"
	//   autoscaling.knative.dev/panicThresholdPercentage: "150.0"
	// Only the kpa.autoscaling.knative.dev class autoscaler supports
	// the panicThresholdPercentage annotation
	PanicThresholdPercentageAnnotationKey = GroupName + "/panicThresholdPercentage"
	// PanicThresholdPercentageMin is the minimum allowable panic
	// threshold percentage. The KPA autoscaler's panic feature
	// allows the autoscaler to be more responsive over a smaller
	// time scale when necessary. To prevent flapping, during panic
	// mode the autoscaler never decreases the number of replicas. If
	// the panic threshold was as small as the stable target, the
	// autoscaler would always be panicking and the autoscaler would
	// never scale down. One hundred and ten percent is about the
	// smallest useful value.
	PanicThresholdPercentageMin = 110.0

	// PanicThresholdPercentageMax is the counterpart to the PanicThresholdPercentageMin
	// but bounding from above.
	PanicThresholdPercentageMax = 1000.0

	// KPALabelKey is the label key attached to a K8s Service to hint to the KPA
	// which services/endpoints should trigger reconciles.
	KPALabelKey = GroupName + "/kpa"
)
<commit_msg>Fix some typos in comments (#6678)<commit_after>/*
Copyright 2018 The Knative 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 autoscaling

import "time"

const (
	// The internal autoscaling group name. This is used for CRDs.
	InternalGroupName = "autoscaling.internal.knative.dev"

	// The public autoscaling group name. This is used for annotations, labels, etc.
	GroupName = "autoscaling.knative.dev"

	// ClassAnnotationKey is the annotation for the explicit class of autoscaler
	// that a particular resource has opted into. For example,
	//   autoscaling.knative.dev/class: foo
	// This uses a different domain because unlike the resource, it is user-facing.
	ClassAnnotationKey = GroupName + "/class"
	// KPA is Knative Horizontal Pod Autoscaler
	KPA = "kpa.autoscaling.knative.dev"
	// HPA is Kubernetes Horizontal Pod Autoscaler
	HPA = "hpa.autoscaling.knative.dev"

	// MinScaleAnnotationKey is the annotation to specify the minimum number of Pods
	// the PodAutoscaler should provision. For example,
	//   autoscaling.knative.dev/minScale: "1"
	MinScaleAnnotationKey = GroupName + "/minScale"
	// MaxScaleAnnotationKey is the annotation to specify the maximum number of Pods
	// the PodAutoscaler should provision. For example,
	//   autoscaling.knative.dev/maxScale: "10"
	MaxScaleAnnotationKey = GroupName + "/maxScale"

	// MetricAnnotationKey is the annotation to specify what metric the PodAutoscaler
	// should be scaled on. For example,
	//   autoscaling.knative.dev/metric: cpu
	MetricAnnotationKey = GroupName + "/metric"
	// Concurrency is the number of requests in-flight at any given time.
	Concurrency = "concurrency"
	// CPU is the amount of the requested cpu actually being consumed by the Pod.
	CPU = "cpu"
	// RPS is the requests per second reaching the Pod.
	RPS = "rps"

	// TargetAnnotationKey is the annotation to specify what metric value the
	// PodAutoscaler should attempt to maintain. For example,
	//   autoscaling.knative.dev/metric: cpu
	//   autoscaling.knative.dev/target: "75"   # target 75% cpu utilization
	TargetAnnotationKey = GroupName + "/target"
	// TargetMin is the minimum allowable target. Values less than
	// zero don't make sense.
	TargetMin = 1

	// WindowAnnotationKey is the annotation to specify the time
	// interval over which to calculate the average metric.  Larger
	// values result in more smoothing. For example,
	//   autoscaling.knative.dev/metric: concurrency
	//   autoscaling.knative.dev/window: "2m"
	// Only the kpa.autoscaling.knative.dev class autoscaler supports
	// the window annotation.
	WindowAnnotationKey = GroupName + "/window"
	// WindowMin is the minimum allowable stable autoscaling
	// window. KPA-class autoscalers calculate the desired replica
	// count every 2 seconds (tick-interval in config-autoscaler) so
	// the closer the window gets to that value, the more likely data
	// points will be missed entirely by the panic window which is
	// smaller than the stable window. Anything less than 6 seconds
	// isn't going to work well.
	WindowMin = 6 * time.Second
	// WindowMax is the maximum permitted stable autoscaling window.
	// This keeps the event horizon to a resonable enough limit.
	WindowMax = 1 * time.Hour

	// TargetUtilizationPercentageKey is the annotation which specifies the
	// desired target resource utilization for the revision.
	// TargetUtilization is a percentage in the 1 <= TU <= 100 range.
	// This annotation takes precedence over the config map value.
	TargetUtilizationPercentageKey = GroupName + "/targetUtilizationPercentage"

	// TargetBurstCapacityKey specifies the desired burst capacity for the
	// revision. Possible values are:
	// -1 -- infinite;
	//  0 -- no TBC;
	// >0 -- actual TBC.
	// <0 && != -1 -- an error.
	TargetBurstCapacityKey = GroupName + "/targetBurstCapacity"

	// PanicWindowPercentageAnnotationKey is the annotation to
	// specify the time interval over which to calculate the average
	// metric during a spike. Where a spike is defined as the metric
	// reaching panic level within the panic window (e.g. panic
	// mode). Lower values make panic mode more sensitive. Note:
	// Panic threshold can be overridden with the
	// PanicThresholdPercentageAnnotationKey. For example,
	//   autoscaling.knative.dev/panicWindowPercentage: "5.0"
	//   autoscaling.knative.dev/panicThresholdPercentage: "150.0"
	// Only the kpa.autoscaling.knative.dev class autoscaler supports
	// the panicWindowPercentage annotation.
	// Panic window is specified as a percentage to maintain the
	// autoscaler's algorithm behavior when only the stable window is
	// specified. The panic window will change along with the stable
	// window at the default percentage.
	PanicWindowPercentageAnnotationKey = GroupName + "/panicWindowPercentage"
	// PanicWindowPercentageMin is the minimum allowable panic window
	// percentage. The autoscaler calculates desired replicas every 2
	// seconds (tick-interval in config-autoscaler), so a panic
	// window less than 2 seconds will be missing data points. One
	// percent is a very small ratio and would require a stable
	// window of at least 3.4 minutes. Anything less doesn't make
	// sense.
	PanicWindowPercentageMin = 1.0
	// PanicWindowPercentageMax is the maximum allowable panic window
	// percentage. The KPA autoscaler's panic feature allows the
	// autoscaler to be more responsive over a smaller time scale
	// when necessary. So the panic window cannot be larger than the
	// stable window.
	PanicWindowPercentageMax = 100.0

	// PanicThresholdPercentageAnnotationKey is the annotation to specify
	// the level at what level panic mode will engage when reached within
	// in the panic window. The level is defined as a percentage of
	// the metric target. Lower values make panic mode more
	// sensitive. For example,
	//   autoscaling.knative.dev/panicWindowPercentage: "5.0"
	//   autoscaling.knative.dev/panicThresholdPercentage: "150.0"
	// Only the kpa.autoscaling.knative.dev class autoscaler supports
	// the panicThresholdPercentage annotation
	PanicThresholdPercentageAnnotationKey = GroupName + "/panicThresholdPercentage"
	// PanicThresholdPercentageMin is the minimum allowable panic
	// threshold percentage. The KPA autoscaler's panic feature
	// allows the autoscaler to be more responsive over a smaller
	// time scale when necessary. To prevent flapping, during panic
	// mode the autoscaler never decreases the number of replicas. If
	// the panic threshold was as small as the stable target, the
	// autoscaler would always be panicking and the autoscaler would
	// never scale down. One hundred and ten percent is about the
	// smallest useful value.
	PanicThresholdPercentageMin = 110.0

	// PanicThresholdPercentageMax is the counterpart to the PanicThresholdPercentageMin
	// but bounding from above.
	PanicThresholdPercentageMax = 1000.0

	// KPALabelKey is the label key attached to a K8s Service to hint to the KPA
	// which services/endpoints should trigger reconciles.
	KPALabelKey = GroupName + "/kpa"
)
<|endoftext|>


<commit_before>package html

import (
	"fmt"
	"github.com/vlifesystems/rulehunter/config"
	"github.com/vlifesystems/rulehunter/html/cmd"
	"github.com/vlifesystems/rulehunter/internal/testhelpers"
	"github.com/vlifesystems/rulehunter/progress"
	"github.com/vlifesystems/rulehunter/quitter"
	"os"
	"path/filepath"
	"testing"
	"time"
)

// This checks if Run will quit properly when told to
func TestRun_quit(t *testing.T) {
	quit := quitter.New()
	l := testhelpers.NewLogger()
	htmlCmds := make(chan cmd.Cmd)
	wd, err := os.Getwd()
	if err != nil {
		t.Fatal("Getwd() err: ", err)
	}
	defer os.Chdir(wd)
	cfgDir := testhelpers.BuildConfigDirs(t, true)
	defer os.RemoveAll(cfgDir)

	pm, err := progress.NewMonitor(
		filepath.Join(cfgDir, "build", "progress"),
		htmlCmds,
	)
	if err != nil {
		t.Fatalf("NewMonitor() err: %v", err)
	}
	config := &config.Config{
		ExperimentsDir:    filepath.Join(cfgDir, "experiments"),
		WWWDir:            filepath.Join(cfgDir, "www"),
		BuildDir:          filepath.Join(cfgDir, "build"),
		MaxNumReportRules: 100,
	}
	hasQuitC := make(chan bool)
	go func() {
		Run(config, pm, l, quit, htmlCmds)
		hasQuitC <- true
	}()

	flushC := time.NewTimer(time.Second).C
	quitC := time.NewTimer(2 * time.Second).C
	timeoutC := time.NewTimer(5 * time.Second).C
	for {
		select {
		case <-flushC:
			htmlCmds <- cmd.Flush
		case <-quitC:
			quit.Quit()
		case <-timeoutC:
			t.Fatalf("Run() didn't quit")
		case <-hasQuitC:
			return
		}
	}
}

// Tests Run for cmd.Reports
func TestRun_cmd_reports(t *testing.T) {
	quit := quitter.New()
	l := testhelpers.NewLogger()
	htmlCmds := make(chan cmd.Cmd)
	wd, err := os.Getwd()
	if err != nil {
		t.Fatal("Getwd() err: ", err)
	}
	defer os.Chdir(wd)
	cfgDir := testhelpers.BuildConfigDirs(t, true)
	defer os.RemoveAll(cfgDir)
	testhelpers.CopyFile(
		t,
		filepath.Join("fixtures", "reports", "bank-loss.json"),
		filepath.Join(cfgDir, "build", "reports"),
	)
	testhelpers.CopyFile(
		t,
		filepath.Join("fixtures", "reports", "bank-profit.json"),
		filepath.Join(cfgDir, "build", "reports"),
	)
	testhelpers.CopyFile(
		t,
		filepath.Join("fixtures", "descriptions", "bank-loss.json"),
		filepath.Join(cfgDir, "build", "descriptions"),
	)
	testhelpers.CopyFile(
		t,
		filepath.Join("fixtures", "descriptions", "bank-profit.json"),
		filepath.Join(cfgDir, "build", "descriptions"),
	)

	pm, err := progress.NewMonitor(
		filepath.Join(cfgDir, "build", "progress"),
		htmlCmds,
	)
	if err != nil {
		t.Fatalf("NewMonitor() err: %v", err)
	}
	config := &config.Config{
		ExperimentsDir:    filepath.Join(cfgDir, "experiments"),
		WWWDir:            filepath.Join(cfgDir, "www"),
		BuildDir:          filepath.Join(cfgDir, "build"),
		MaxNumReportRules: 100,
	}
	wantFiles := []string{
		filepath.Join(cfgDir, "www", "index.html"),
		filepath.Join(cfgDir, "www", "activity", "index.html"),
		filepath.Join(cfgDir, "www", "licence", "index.html"),
		filepath.Join(cfgDir, "www", "reports", "how-to-make-a-loss",
			"index.html"),
		filepath.Join(cfgDir, "www", "reports", "how-to-make-a-profit",
			"index.html"),
		filepath.Join(cfgDir, "www", "tag", "test",
			"index.html"),
		filepath.Join(cfgDir, "www", "tag", "bank",
			"index.html"),
		filepath.Join(cfgDir, "www", "tag", "fahrenheit-451",
			"index.html"),
		filepath.Join(cfgDir, "www", "tag", "fred-ned",
			"index.html"),
		filepath.Join(cfgDir, "www", "tag", "hot-in-the-city",
			"index.html"),
	}

	hasQuitC := make(chan bool)
	go func() {
		Run(config, pm, l, quit, htmlCmds)
		hasQuitC <- true
	}()

	reportsC := time.NewTimer(time.Second).C
	timeoutC := time.NewTimer(5 * time.Second).C
	ticker := time.NewTicker(time.Millisecond * 100).C
	for {
		select {
		case <-reportsC:
			htmlCmds <- cmd.Reports
		case <-ticker:
			if err := checkFilesExist(wantFiles); err == nil {
				quit.Quit()
			}
		case <-timeoutC:
			quit.Quit()
			if err := checkFilesExist(wantFiles); err != nil {
				t.Fatalf("Run: %s", err)
			}
		case <-hasQuitC:
			return
		}
	}
}

// Tests Run for cmd.Progress
func TestRun_cmd_progress(t *testing.T) {
	quit := quitter.New()
	l := testhelpers.NewLogger()
	htmlCmds := make(chan cmd.Cmd)
	wd, err := os.Getwd()
	if err != nil {
		t.Fatal("Getwd() err: ", err)
	}
	defer os.Chdir(wd)
	cfgDir := testhelpers.BuildConfigDirs(t, true)
	defer os.RemoveAll(cfgDir)
	testhelpers.CopyFile(
		t,
		filepath.Join("fixtures", "reports", "bank-loss.json"),
		filepath.Join(cfgDir, "build", "reports"),
	)
	testhelpers.CopyFile(
		t,
		filepath.Join("fixtures", "reports", "bank-profit.json"),
		filepath.Join(cfgDir, "build", "reports"),
	)
	testhelpers.CopyFile(
		t,
		filepath.Join("fixtures", "descriptions", "bank-loss.json"),
		filepath.Join(cfgDir, "build", "descriptions"),
	)
	testhelpers.CopyFile(
		t,
		filepath.Join("fixtures", "descriptions", "bank-profit.json"),
		filepath.Join(cfgDir, "build", "descriptions"),
	)

	pm, err := progress.NewMonitor(
		filepath.Join(cfgDir, "build", "progress"),
		htmlCmds,
	)
	if err != nil {
		t.Fatalf("NewMonitor() err: %v", err)
	}
	config := &config.Config{
		ExperimentsDir:    filepath.Join(cfgDir, "experiments"),
		WWWDir:            filepath.Join(cfgDir, "www"),
		BuildDir:          filepath.Join(cfgDir, "build"),
		MaxNumReportRules: 100,
	}
	wantFiles := []string{
		filepath.Join(cfgDir, "www", "activity", "index.html"),
	}

	hasQuitC := make(chan bool)
	go func() {
		Run(config, pm, l, quit, htmlCmds)
		hasQuitC <- true
	}()

	progressC := time.NewTimer(time.Second).C
	timeoutC := time.NewTimer(5 * time.Second).C
	ticker := time.NewTicker(time.Millisecond * 100).C
	for {
		select {
		case <-progressC:
			htmlCmds <- cmd.Progress
		case <-ticker:
			if err := checkFilesExist(wantFiles); err == nil {
				quit.Quit()
			}
		case <-timeoutC:
			quit.Quit()
			if err := checkFilesExist(wantFiles); err != nil {
				t.Fatalf("Run: %s", err)
			}
		case <-hasQuitC:
			return
		}
	}
}

func TestGenReportURLDir(t *testing.T) {
	cases := []struct {
		title   string
		wantDir string
	}{
		{"This could be very interesting",
			"reports/this-could-be-very-interesting/",
		},
	}
	for _, c := range cases {
		got := genReportURLDir(c.title)
		if got != c.wantDir {
			t.Errorf("genReportFilename(%s) got: %s, want: %s",
				c.title, got, c.wantDir)
		}
	}
}

func TestEscapeString(t *testing.T) {
	cases := []struct {
		in   string
		want string
	}{
		{"This is a TITLE",
			"this-is-a-title"},
		{"  hello how are % you423 33  today __ --",
			"hello-how-are-you423-33-today"},
		{"--  hello how are %^& you423 33  today __ --",
			"hello-how-are-you423-33-today"},
		{"hello((_ how are % you423 33  today",
			"hello-how-are-you423-33-today"},
		{"This is it's TITLE",
			"this-is-its-title"},
		{"", ""},
	}
	for _, c := range cases {
		got := escapeString(c.in)
		if got != c.want {
			t.Errorf("escapeString(%s) got: %s, want: %s", c.in, got, c.want)
		}
	}
}

/***********************************************
 *   Helper Functions
 ***********************************************/

func checkFilesExist(wantFiles []string) error {
	for _, wantFile := range wantFiles {
		if _, err := os.Stat(wantFile); os.IsNotExist(err) {
			return fmt.Errorf("file doesn't exist: %s", wantFile)
		}
	}
	return nil
}
<commit_msg>Fix html.Run test to remove initial cmd.All files<commit_after>package html

import (
	"fmt"
	"github.com/vlifesystems/rulehunter/config"
	"github.com/vlifesystems/rulehunter/html/cmd"
	"github.com/vlifesystems/rulehunter/internal/testhelpers"
	"github.com/vlifesystems/rulehunter/progress"
	"github.com/vlifesystems/rulehunter/quitter"
	"os"
	"path/filepath"
	"testing"
	"time"
)

// This checks if Run will quit properly when told to
func TestRun_quit(t *testing.T) {
	quit := quitter.New()
	l := testhelpers.NewLogger()
	htmlCmds := make(chan cmd.Cmd)
	wd, err := os.Getwd()
	if err != nil {
		t.Fatal("Getwd() err: ", err)
	}
	defer os.Chdir(wd)
	cfgDir := testhelpers.BuildConfigDirs(t, true)
	defer os.RemoveAll(cfgDir)

	pm, err := progress.NewMonitor(
		filepath.Join(cfgDir, "build", "progress"),
		htmlCmds,
	)
	if err != nil {
		t.Fatalf("NewMonitor() err: %v", err)
	}
	config := &config.Config{
		ExperimentsDir:    filepath.Join(cfgDir, "experiments"),
		WWWDir:            filepath.Join(cfgDir, "www"),
		BuildDir:          filepath.Join(cfgDir, "build"),
		MaxNumReportRules: 100,
	}
	hasQuitC := make(chan bool)
	go func() {
		Run(config, pm, l, quit, htmlCmds)
		hasQuitC <- true
	}()

	flushC := time.NewTimer(time.Second).C
	quitC := time.NewTimer(2 * time.Second).C
	timeoutC := time.NewTimer(5 * time.Second).C
	for {
		select {
		case <-flushC:
			htmlCmds <- cmd.Flush
		case <-quitC:
			quit.Quit()
		case <-timeoutC:
			t.Fatalf("Run() didn't quit")
		case <-hasQuitC:
			return
		}
	}
}

// Tests Run for cmd.All
func TestRun_cmd_all(t *testing.T) {
	quit := quitter.New()
	l := testhelpers.NewLogger()
	htmlCmds := make(chan cmd.Cmd)
	wd, err := os.Getwd()
	if err != nil {
		t.Fatal("Getwd() err: ", err)
	}
	defer os.Chdir(wd)
	cfgDir := testhelpers.BuildConfigDirs(t, true)
	defer os.RemoveAll(cfgDir)
	testhelpers.CopyFile(
		t,
		filepath.Join("fixtures", "reports", "bank-loss.json"),
		filepath.Join(cfgDir, "build", "reports"),
	)
	testhelpers.CopyFile(
		t,
		filepath.Join("fixtures", "reports", "bank-profit.json"),
		filepath.Join(cfgDir, "build", "reports"),
	)
	testhelpers.CopyFile(
		t,
		filepath.Join("fixtures", "descriptions", "bank-loss.json"),
		filepath.Join(cfgDir, "build", "descriptions"),
	)
	testhelpers.CopyFile(
		t,
		filepath.Join("fixtures", "descriptions", "bank-profit.json"),
		filepath.Join(cfgDir, "build", "descriptions"),
	)

	pm, err := progress.NewMonitor(
		filepath.Join(cfgDir, "build", "progress"),
		htmlCmds,
	)
	if err != nil {
		t.Fatalf("NewMonitor() err: %v", err)
	}
	config := &config.Config{
		ExperimentsDir:    filepath.Join(cfgDir, "experiments"),
		WWWDir:            filepath.Join(cfgDir, "www"),
		BuildDir:          filepath.Join(cfgDir, "build"),
		MaxNumReportRules: 100,
	}
	wantFiles := []string{
		filepath.Join(cfgDir, "www", "index.html"),
		filepath.Join(cfgDir, "www", "activity", "index.html"),
		filepath.Join(cfgDir, "www", "licence", "index.html"),
		filepath.Join(cfgDir, "www", "reports", "how-to-make-a-loss",
			"index.html"),
		filepath.Join(cfgDir, "www", "reports", "how-to-make-a-profit",
			"index.html"),
		filepath.Join(cfgDir, "www", "tag", "test",
			"index.html"),
		filepath.Join(cfgDir, "www", "tag", "bank",
			"index.html"),
		filepath.Join(cfgDir, "www", "tag", "fahrenheit-451",
			"index.html"),
		filepath.Join(cfgDir, "www", "tag", "fred-ned",
			"index.html"),
		filepath.Join(cfgDir, "www", "tag", "hot-in-the-city",
			"index.html"),
	}

	go Run(config, pm, l, quit, htmlCmds)
	defer quit.Quit()

	var allC <-chan time.Time
	timeoutC := time.NewTimer(5 * time.Second).C
	ticker := time.NewTicker(time.Millisecond * 100).C
	filesRemoved := false
	for {
		select {
		case <-allC:
			htmlCmds <- cmd.All
		case <-ticker:
			if err := checkFilesExist(wantFiles); err == nil {
				if !filesRemoved {
					// Files are first removed because cmd.All is passed at start
					if err := removeFiles(wantFiles); err != nil {
						t.Fatalf("removeFiles: %s", err)
					}
					filesRemoved = true
					allC = time.NewTimer(time.Second).C
				} else {
					return
				}
			}
		case <-timeoutC:
			if err := checkFilesExist(wantFiles); err != nil {
				t.Fatalf("Run: %s", err)
			}
			return
		}
	}
}

// Tests Run for cmd.Reports
func TestRun_cmd_reports(t *testing.T) {
	quit := quitter.New()
	l := testhelpers.NewLogger()
	htmlCmds := make(chan cmd.Cmd)
	wd, err := os.Getwd()
	if err != nil {
		t.Fatal("Getwd() err: ", err)
	}
	defer os.Chdir(wd)
	cfgDir := testhelpers.BuildConfigDirs(t, true)
	defer os.RemoveAll(cfgDir)
	testhelpers.CopyFile(
		t,
		filepath.Join("fixtures", "reports", "bank-loss.json"),
		filepath.Join(cfgDir, "build", "reports"),
	)
	testhelpers.CopyFile(
		t,
		filepath.Join("fixtures", "reports", "bank-profit.json"),
		filepath.Join(cfgDir, "build", "reports"),
	)
	testhelpers.CopyFile(
		t,
		filepath.Join("fixtures", "descriptions", "bank-loss.json"),
		filepath.Join(cfgDir, "build", "descriptions"),
	)
	testhelpers.CopyFile(
		t,
		filepath.Join("fixtures", "descriptions", "bank-profit.json"),
		filepath.Join(cfgDir, "build", "descriptions"),
	)

	pm, err := progress.NewMonitor(
		filepath.Join(cfgDir, "build", "progress"),
		htmlCmds,
	)
	if err != nil {
		t.Fatalf("NewMonitor() err: %v", err)
	}
	config := &config.Config{
		ExperimentsDir:    filepath.Join(cfgDir, "experiments"),
		WWWDir:            filepath.Join(cfgDir, "www"),
		BuildDir:          filepath.Join(cfgDir, "build"),
		MaxNumReportRules: 100,
	}
	wantFiles := []string{
		filepath.Join(cfgDir, "www", "index.html"),
		filepath.Join(cfgDir, "www", "activity", "index.html"),
		filepath.Join(cfgDir, "www", "reports", "how-to-make-a-loss",
			"index.html"),
		filepath.Join(cfgDir, "www", "reports", "how-to-make-a-profit",
			"index.html"),
		filepath.Join(cfgDir, "www", "tag", "test",
			"index.html"),
		filepath.Join(cfgDir, "www", "tag", "bank",
			"index.html"),
		filepath.Join(cfgDir, "www", "tag", "fahrenheit-451",
			"index.html"),
		filepath.Join(cfgDir, "www", "tag", "fred-ned",
			"index.html"),
		filepath.Join(cfgDir, "www", "tag", "hot-in-the-city",
			"index.html"),
	}

	go Run(config, pm, l, quit, htmlCmds)
	defer quit.Quit()

	var reportsC <-chan time.Time
	timeoutC := time.NewTimer(5 * time.Second).C
	ticker := time.NewTicker(time.Millisecond * 100).C
	filesRemoved := false
	for {
		select {
		case <-reportsC:
			htmlCmds <- cmd.Reports
		case <-ticker:
			if err := checkFilesExist(wantFiles); err == nil {
				if !filesRemoved {
					// Files are first removed because cmd.All is passed at start
					if err := removeFiles(wantFiles); err != nil {
						t.Fatalf("removeFiles: %s", err)
					}
					filesRemoved = true
					reportsC = time.NewTimer(time.Second).C
				} else {
					return
				}
			}
		case <-timeoutC:
			if err := checkFilesExist(wantFiles); err != nil {
				t.Fatalf("Run: %s", err)
			}
			return
		}
	}
}

// Tests Run for cmd.Progress
func TestRun_cmd_progress(t *testing.T) {
	quit := quitter.New()
	l := testhelpers.NewLogger()
	htmlCmds := make(chan cmd.Cmd)
	wd, err := os.Getwd()
	if err != nil {
		t.Fatal("Getwd() err: ", err)
	}
	defer os.Chdir(wd)
	cfgDir := testhelpers.BuildConfigDirs(t, true)
	defer os.RemoveAll(cfgDir)
	testhelpers.CopyFile(
		t,
		filepath.Join("fixtures", "reports", "bank-loss.json"),
		filepath.Join(cfgDir, "build", "reports"),
	)
	testhelpers.CopyFile(
		t,
		filepath.Join("fixtures", "reports", "bank-profit.json"),
		filepath.Join(cfgDir, "build", "reports"),
	)
	testhelpers.CopyFile(
		t,
		filepath.Join("fixtures", "descriptions", "bank-loss.json"),
		filepath.Join(cfgDir, "build", "descriptions"),
	)
	testhelpers.CopyFile(
		t,
		filepath.Join("fixtures", "descriptions", "bank-profit.json"),
		filepath.Join(cfgDir, "build", "descriptions"),
	)

	pm, err := progress.NewMonitor(
		filepath.Join(cfgDir, "build", "progress"),
		htmlCmds,
	)
	if err != nil {
		t.Fatalf("NewMonitor() err: %v", err)
	}
	config := &config.Config{
		ExperimentsDir:    filepath.Join(cfgDir, "experiments"),
		WWWDir:            filepath.Join(cfgDir, "www"),
		BuildDir:          filepath.Join(cfgDir, "build"),
		MaxNumReportRules: 100,
	}
	wantFiles := []string{
		filepath.Join(cfgDir, "www", "activity", "index.html"),
	}

	go Run(config, pm, l, quit, htmlCmds)
	defer quit.Quit()

	var progressC <-chan time.Time
	timeoutC := time.NewTimer(5 * time.Second).C
	ticker := time.NewTicker(time.Millisecond * 100).C
	filesRemoved := false
	for {
		select {
		case <-progressC:
			htmlCmds <- cmd.Progress
		case <-ticker:
			if err := checkFilesExist(wantFiles); err == nil {
				if !filesRemoved {
					// Files are first removed because cmd.All is passed at start
					if err := removeFiles(wantFiles); err != nil {
						t.Fatalf("removeFiles: %s", err)
					}
					filesRemoved = true
					progressC = time.NewTimer(time.Second).C
				} else {
					return
				}
			}
		case <-timeoutC:
			if err := checkFilesExist(wantFiles); err != nil {
				t.Fatalf("Run: %s", err)
			}
			return
		}
	}
}

func TestGenReportURLDir(t *testing.T) {
	cases := []struct {
		title   string
		wantDir string
	}{
		{"This could be very interesting",
			"reports/this-could-be-very-interesting/",
		},
	}
	for _, c := range cases {
		got := genReportURLDir(c.title)
		if got != c.wantDir {
			t.Errorf("genReportFilename(%s) got: %s, want: %s",
				c.title, got, c.wantDir)
		}
	}
}

func TestEscapeString(t *testing.T) {
	cases := []struct {
		in   string
		want string
	}{
		{"This is a TITLE",
			"this-is-a-title"},
		{"  hello how are % you423 33  today __ --",
			"hello-how-are-you423-33-today"},
		{"--  hello how are %^& you423 33  today __ --",
			"hello-how-are-you423-33-today"},
		{"hello((_ how are % you423 33  today",
			"hello-how-are-you423-33-today"},
		{"This is it's TITLE",
			"this-is-its-title"},
		{"", ""},
	}
	for _, c := range cases {
		got := escapeString(c.in)
		if got != c.want {
			t.Errorf("escapeString(%s) got: %s, want: %s", c.in, got, c.want)
		}
	}
}

/***********************************************
 *   Helper Functions
 ***********************************************/

func checkFilesExist(files []string) error {
	for _, f := range files {
		if _, err := os.Stat(f); os.IsNotExist(err) {
			return fmt.Errorf("file doesn't exist: %s", f)
		}
	}
	return nil
}

func removeFiles(files []string) error {
	for _, f := range files {
		if err := os.Remove(f); err != nil {
			return err
		}
	}
	return nil
}
<|endoftext|>