<commit_before>package main

import (
	"bufio"
	"crypto/md5"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/http/httputil"

	log "github.com/Sirupsen/logrus"
	"github.com/elazarl/goproxy"
	"github.com/garyburd/redigo/redis"
)

type DBClient struct {
	cache Cache
	http  *http.Client
}

// request holds structure for request
type request struct {
	details requestDetails
}

var emptyResp = &http.Response{}

// requestDetails stores information about request, it's used for creating unique hash and also as a payload structure
type requestDetails struct {
	Path        string              `json:"path"`
	Method      string              `json:"method"`
	Destination string              `json:"destination"`
	Query       string              `json:"query"`
	Body        string              `json:"body"`
	RemoteAddr  string              `json:"remoteAddr"`
	Headers     map[string][]string `json:"headers"`
}

// hash returns unique hash key for request
func (r *request) hash() string {
	h := md5.New()
	io.WriteString(h, fmt.Sprintf("%s%s%s%s", r.details.Destination, r.details.Path, r.details.Method, r.details.Query))
	return fmt.Sprintf("%x", h.Sum(nil))
}

// res structure hold response body from external service, body is not decoded and is supposed
// to be bytes, however headers should provide all required information for later decoding
// by the client.
type response struct {
	Status  int                 `json:"status"`
	Body    string              `json:"body"`
	Headers map[string][]string `json:"headers"`
}

// Payload structure holds request and response structure
type Payload struct {
	Response response       `json:"response"`
	Request  requestDetails `json:"request"`
	ID       string         `json:"id"`
}

// recordRequest saves request for later playback
func (d *DBClient) captureRequest(req *http.Request) (*http.Response, error) {

	// forwarding request
	resp, err := d.doRequest(req)

	if err == nil {

		// getting response body
		body, err := httputil.DumpResponse(resp, true)
		if err != nil {
			// copying the response body did not work
			if err != nil {
				log.WithFields(log.Fields{
					"error": err.Error(),
				}).Error("Failed to copy response body.")
			}
		}

		// saving response body with request/response meta to cache
		go d.save(req, resp, body)
	}

	// return new response or error here
	return resp, err
}

// doRequest performs original request and returns response that should be returned to client and error (if there is one)
func (d *DBClient) doRequest(request *http.Request) (*http.Response, error) {
	// We can't have this set. And it only contains "/pkg/net/http/" anyway
	request.RequestURI = ""

	if AppConfig.middleware != "" {
		var payload Payload

		c := NewConstructor(request, payload)
		c.ApplyMiddleware(AppConfig.middleware)

		request = c.reconstructRequest()

	}

	resp, err := d.http.Do(request)

	if err != nil {
		log.WithFields(log.Fields{
			"error":  err.Error(),
			"host":   request.Host,
			"method": request.Method,
			"path":   request.URL.Path,
		}).Error("Could not forward request.")
		return nil, err
	}

	log.WithFields(log.Fields{
		"host":   request.Host,
		"method": request.Method,
		"path":   request.URL.Path,
	}).Info("Response got successfuly!")

	resp.Header.Set("Gen-proxy", "Was-Here")
	return resp, nil

}

// save gets request fingerprint, extracts request body, status code and headers, then saves it to cache
func (d *DBClient) save(req *http.Request, resp *http.Response, respBody []byte) {
	// record request here
	key := getRequestFingerprint(req)

	if resp == nil {
		resp = emptyResp
	} else {
		responseObj := response{
			Status:  resp.StatusCode,
			Body:    string(respBody),
			Headers: resp.Header,
		}

		log.WithFields(log.Fields{
			"path":          req.URL.Path,
			"rawQuery":      req.URL.RawQuery,
			"requestMethod": req.Method,
			"destination":   req.Host,
			"hashKey":       key,
		}).Info("Recording")

		b := bufio.NewScanner(req.Body)

		bodyStr := b.Text()

		requestObj := requestDetails{
			Path:        req.URL.Path,
			Method:      req.Method,
			Destination: req.Host,
			Query:       req.URL.RawQuery,
			Body:        bodyStr,
			RemoteAddr:  req.RemoteAddr,
			Headers:     req.Header,
		}

		payload := Payload{
			Response: responseObj,
			Request:  requestObj,
			ID:       key,
		}
		// converting it to json bytes
		bts, err := json.Marshal(payload)

		if err != nil {
			log.WithFields(log.Fields{
				"error": err.Error(),
			}).Error("Failed to marshal json")
		} else {
			d.cache.set(key, bts)
		}
	}

}

// getAllRecordsRaw returns raw (json string) for all records
func (d *DBClient) getAllRecordsRaw() ([]string, error) {
	keys, err := d.cache.getAllKeys()

	if err == nil {

		// checking if there are any records
		if len(keys) == 0 {
			return nil, nil
		}

		jsonStrs, err := d.cache.getAllValues(keys)

		if err != nil {
			log.WithFields(log.Fields{
				"error": err.Error(),
			}).Error("Failed to get all values (raw)")
			return nil, err
		} else {
			return jsonStrs, nil
		}

	} else {
		return nil, err
	}
}

// getAllRecords returns all stored
func (d *DBClient) getAllRecords() ([]Payload, error) {
	var payloads []Payload

	jsonStrs, err := d.getAllRecordsRaw()

	if err != nil {
		log.WithFields(log.Fields{
			"error": err.Error(),
		}).Error("Failed to get all values")
	} else {

		if jsonStrs != nil {
			for _, v := range jsonStrs {
				var pl Payload
				err = json.Unmarshal([]byte(v), &pl)
				if err != nil {
					log.WithFields(log.Fields{
						"error": err.Error(),
						"json":  v,
					}).Warning("Failed to deserialize json")
				} else {
					payloads = append(payloads, pl)
				}
			}
		}
	}

	return payloads, err

}

// deleteAllRecords deletes all recorded requests
func (d *DBClient) deleteAllRecords() error {
	keys, err := d.cache.getAllKeys()

	if err != nil {
		log.WithFields(log.Fields{
			"error": err.Error(),
		}).Warning("Failed to get keys, cannot delete all records")
		return err
	} else {
		for _, v := range keys {
			d.cache.delete(v)
		}
		return nil
	}
}

// getRequestFingerprint returns request hash
func getRequestFingerprint(req *http.Request) string {
	details := requestDetails{Path: req.URL.Path, Method: req.Method, Destination: req.Host, Query: req.URL.RawQuery}
	r := request{details: details}
	return r.hash()
}

// getResponse returns stored response from cache
func (d *DBClient) getResponse(req *http.Request) *http.Response {

	key := getRequestFingerprint(req)
	var payload Payload

	payloadBts, err := redis.Bytes(d.cache.get(key))

	if err == nil {
		// getting cache response
		err = json.Unmarshal(payloadBts, &payload)
		if err != nil {
			log.Error(err)
			// what now?
		}

		c := NewConstructor(req, payload)

		if AppConfig.middleware != "" {
			_ = c.ApplyMiddleware(AppConfig.middleware)
		}

		response := c.reconstructResponse()

		log.WithFields(log.Fields{
			"key":        key,
			"status":     payload.Response.Status,
			"bodyLength": response.ContentLength,
			"mode":       AppConfig.mode,
		}).Info("Response found, returning")

		return response

	} else {
		log.WithFields(log.Fields{
			"error": err.Error(),
			"mode":  AppConfig.mode,
		}).Error("Failed to retrieve response from cache")
		// return error? if we return nil - proxy forwards request to original destination
		return goproxy.NewResponse(req,
			goproxy.ContentTypeText, http.StatusPreconditionFailed,
			"Coudldn't find recorded request, please record it first!")
	}

}
<commit_msg>renaming genproxy -> hoverfly<commit_after>package main

import (
	"bufio"
	"crypto/md5"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/http/httputil"

	log "github.com/Sirupsen/logrus"
	"github.com/elazarl/goproxy"
	"github.com/garyburd/redigo/redis"
)

type DBClient struct {
	cache Cache
	http  *http.Client
}

// request holds structure for request
type request struct {
	details requestDetails
}

var emptyResp = &http.Response{}

// requestDetails stores information about request, it's used for creating unique hash and also as a payload structure
type requestDetails struct {
	Path        string              `json:"path"`
	Method      string              `json:"method"`
	Destination string              `json:"destination"`
	Query       string              `json:"query"`
	Body        string              `json:"body"`
	RemoteAddr  string              `json:"remoteAddr"`
	Headers     map[string][]string `json:"headers"`
}

// hash returns unique hash key for request
func (r *request) hash() string {
	h := md5.New()
	io.WriteString(h, fmt.Sprintf("%s%s%s%s", r.details.Destination, r.details.Path, r.details.Method, r.details.Query))
	return fmt.Sprintf("%x", h.Sum(nil))
}

// res structure hold response body from external service, body is not decoded and is supposed
// to be bytes, however headers should provide all required information for later decoding
// by the client.
type response struct {
	Status  int                 `json:"status"`
	Body    string              `json:"body"`
	Headers map[string][]string `json:"headers"`
}

// Payload structure holds request and response structure
type Payload struct {
	Response response       `json:"response"`
	Request  requestDetails `json:"request"`
	ID       string         `json:"id"`
}

// recordRequest saves request for later playback
func (d *DBClient) captureRequest(req *http.Request) (*http.Response, error) {

	// forwarding request
	resp, err := d.doRequest(req)

	if err == nil {

		// getting response body
		body, err := httputil.DumpResponse(resp, true)
		if err != nil {
			// copying the response body did not work
			if err != nil {
				log.WithFields(log.Fields{
					"error": err.Error(),
				}).Error("Failed to copy response body.")
			}
		}

		// saving response body with request/response meta to cache
		go d.save(req, resp, body)
	}

	// return new response or error here
	return resp, err
}

// doRequest performs original request and returns response that should be returned to client and error (if there is one)
func (d *DBClient) doRequest(request *http.Request) (*http.Response, error) {
	// We can't have this set. And it only contains "/pkg/net/http/" anyway
	request.RequestURI = ""

	if AppConfig.middleware != "" {
		var payload Payload

		c := NewConstructor(request, payload)
		c.ApplyMiddleware(AppConfig.middleware)

		request = c.reconstructRequest()

	}

	resp, err := d.http.Do(request)

	if err != nil {
		log.WithFields(log.Fields{
			"error":  err.Error(),
			"host":   request.Host,
			"method": request.Method,
			"path":   request.URL.Path,
		}).Error("Could not forward request.")
		return nil, err
	}

	log.WithFields(log.Fields{
		"host":   request.Host,
		"method": request.Method,
		"path":   request.URL.Path,
	}).Info("Response got successfuly!")

	resp.Header.Set("hoverfly", "Was-Here")
	return resp, nil

}

// save gets request fingerprint, extracts request body, status code and headers, then saves it to cache
func (d *DBClient) save(req *http.Request, resp *http.Response, respBody []byte) {
	// record request here
	key := getRequestFingerprint(req)

	if resp == nil {
		resp = emptyResp
	} else {
		responseObj := response{
			Status:  resp.StatusCode,
			Body:    string(respBody),
			Headers: resp.Header,
		}

		log.WithFields(log.Fields{
			"path":          req.URL.Path,
			"rawQuery":      req.URL.RawQuery,
			"requestMethod": req.Method,
			"destination":   req.Host,
			"hashKey":       key,
		}).Info("Recording")

		b := bufio.NewScanner(req.Body)

		bodyStr := b.Text()

		requestObj := requestDetails{
			Path:        req.URL.Path,
			Method:      req.Method,
			Destination: req.Host,
			Query:       req.URL.RawQuery,
			Body:        bodyStr,
			RemoteAddr:  req.RemoteAddr,
			Headers:     req.Header,
		}

		payload := Payload{
			Response: responseObj,
			Request:  requestObj,
			ID:       key,
		}
		// converting it to json bytes
		bts, err := json.Marshal(payload)

		if err != nil {
			log.WithFields(log.Fields{
				"error": err.Error(),
			}).Error("Failed to marshal json")
		} else {
			d.cache.set(key, bts)
		}
	}

}

// getAllRecordsRaw returns raw (json string) for all records
func (d *DBClient) getAllRecordsRaw() ([]string, error) {
	keys, err := d.cache.getAllKeys()

	if err == nil {

		// checking if there are any records
		if len(keys) == 0 {
			return nil, nil
		}

		jsonStrs, err := d.cache.getAllValues(keys)

		if err != nil {
			log.WithFields(log.Fields{
				"error": err.Error(),
			}).Error("Failed to get all values (raw)")
			return nil, err
		} else {
			return jsonStrs, nil
		}

	} else {
		return nil, err
	}
}

// getAllRecords returns all stored
func (d *DBClient) getAllRecords() ([]Payload, error) {
	var payloads []Payload

	jsonStrs, err := d.getAllRecordsRaw()

	if err != nil {
		log.WithFields(log.Fields{
			"error": err.Error(),
		}).Error("Failed to get all values")
	} else {

		if jsonStrs != nil {
			for _, v := range jsonStrs {
				var pl Payload
				err = json.Unmarshal([]byte(v), &pl)
				if err != nil {
					log.WithFields(log.Fields{
						"error": err.Error(),
						"json":  v,
					}).Warning("Failed to deserialize json")
				} else {
					payloads = append(payloads, pl)
				}
			}
		}
	}

	return payloads, err

}

// deleteAllRecords deletes all recorded requests
func (d *DBClient) deleteAllRecords() error {
	keys, err := d.cache.getAllKeys()

	if err != nil {
		log.WithFields(log.Fields{
			"error": err.Error(),
		}).Warning("Failed to get keys, cannot delete all records")
		return err
	} else {
		for _, v := range keys {
			d.cache.delete(v)
		}
		return nil
	}
}

// getRequestFingerprint returns request hash
func getRequestFingerprint(req *http.Request) string {
	details := requestDetails{Path: req.URL.Path, Method: req.Method, Destination: req.Host, Query: req.URL.RawQuery}
	r := request{details: details}
	return r.hash()
}

// getResponse returns stored response from cache
func (d *DBClient) getResponse(req *http.Request) *http.Response {

	key := getRequestFingerprint(req)
	var payload Payload

	payloadBts, err := redis.Bytes(d.cache.get(key))

	if err == nil {
		// getting cache response
		err = json.Unmarshal(payloadBts, &payload)
		if err != nil {
			log.Error(err)
			// what now?
		}

		c := NewConstructor(req, payload)

		if AppConfig.middleware != "" {
			_ = c.ApplyMiddleware(AppConfig.middleware)
		}

		response := c.reconstructResponse()

		log.WithFields(log.Fields{
			"key":        key,
			"status":     payload.Response.Status,
			"bodyLength": response.ContentLength,
			"mode":       AppConfig.mode,
		}).Info("Response found, returning")

		return response

	} else {
		log.WithFields(log.Fields{
			"error": err.Error(),
			"mode":  AppConfig.mode,
		}).Error("Failed to retrieve response from cache")
		// return error? if we return nil - proxy forwards request to original destination
		return goproxy.NewResponse(req,
			goproxy.ContentTypeText, http.StatusPreconditionFailed,
			"Coudldn't find recorded request, please record it first!")
	}

}
<|endoftext|>


<commit_before>package utp

/*
#include "utp.h"
*/
import "C"
import (
	"errors"
	"log"
	"net"
	"time"
)

type Socket struct {
	pc            net.PacketConn
	ctx           *C.utp_context
	backlog       chan *Conn
	closed        bool
	conns         map[*C.utp_socket]*Conn
	nonUtpReads   chan packet
	writeDeadline time.Time
	readDeadline  time.Time
}

var (
	_ net.PacketConn = (*Socket)(nil)
)

type packet struct {
	b    []byte
	from net.Addr
}

func NewSocket(network, addr string) (*Socket, error) {
	pc, err := net.ListenPacket(network, addr)
	if err != nil {
		return nil, err
	}
	mu.Lock()
	defer mu.Unlock()
	ctx := C.utp_init(2)
	if ctx == nil {
		panic(ctx)
	}
	ctx.setCallbacks()
	// ctx.setOption(C.UTP_LOG_NORMAL, 1)
	// ctx.setOption(C.UTP_LOG_MTU, 1)
	// ctx.setOption(C.UTP_LOG_DEBUG, 1)
	s := &Socket{
		pc:          pc,
		ctx:         ctx,
		backlog:     make(chan *Conn, 5),
		conns:       make(map[*C.utp_socket]*Conn),
		nonUtpReads: make(chan packet, 10),
	}
	libContextToSocket[ctx] = s
	go s.packetReader()
	return s, nil
}

func (s *Socket) onLibSocketDestroyed(ls *C.utp_socket) {
	delete(s.conns, ls)
}

func (s *Socket) newConn(us *C.utp_socket) *Conn {
	c := &Conn{
		s: us,
	}
	c.cond.L = &mu
	s.conns[us] = c
	return c
}

func (s *Socket) packetReader() {
	for {
		b := make([]byte, 0x1000)
		n, addr, err := s.pc.ReadFrom(b)
		if err != nil {
			mu.Lock()
			closed := s.closed
			mu.Unlock()
			if closed {
				return
			}
			panic(err)
		}
		sa, sal := netAddrToLibSockaddr(addr)
		func() {
			mu.Lock()
			defer mu.Unlock()
			if s.closed {
				return
			}
			if C.utp_process_udp(s.ctx, (*C.byte)(&b[0]), C.size_t(n), sa, sal) != 0 {
				socketUtpPacketsReceived.Add(1)
			} else {
				s.onReadNonUtp(b[:n], addr)
			}
			C.utp_issue_deferred_acks(s.ctx)
			C.utp_check_timeouts(s.ctx)
		}()
	}
}

func (s *Socket) timeoutChecker() {
	for {
		mu.Lock()
		if s.closed {
			mu.Unlock()
			return
		}
		C.utp_check_timeouts(s.ctx)
		mu.Unlock()
		time.Sleep(500 * time.Millisecond)
	}
}

func (me *Socket) Close() error {
	mu.Lock()
	defer mu.Unlock()
	if me.closed {
		return nil
	}
	C.utp_destroy(me.ctx)
	me.ctx = nil
	me.pc.Close()
	close(me.backlog)
	close(me.nonUtpReads)
	me.closed = true
	return nil
}

func (me *Socket) Addr() net.Addr {
	return me.pc.LocalAddr()
}

func (me *Socket) LocalAddr() net.Addr {
	return me.pc.LocalAddr()
}

func (s *Socket) Accept() (net.Conn, error) {
	nc, ok := <-s.backlog
	if !ok {
		return nil, errors.New("closed")
	}
	return nc, nil
}

func (s *Socket) Dial(addr string) (net.Conn, error) {
	return s.DialTimeout(addr, 0)
}

func (s *Socket) DialTimeout(addr string, timeout time.Duration) (net.Conn, error) {
	ua, err := net.ResolveUDPAddr(s.Addr().Network(), addr)
	if err != nil {
		panic(err)
	}
	sa, sl := netAddrToLibSockaddr(ua)
	mu.Lock()
	c := s.newConn(C.utp_create_socket(s.ctx))
	C.utp_connect(c.s, sa, sl)
	defer mu.Unlock()
	err = c.waitForConnect()
	if err != nil {
		c.close()
		return nil, err
	}
	return c, nil
}

func (s *Socket) pushBacklog(c *Conn) {
	select {
	case s.backlog <- c:
	default:
		c.Close()
	}
}

func (s *Socket) ReadFrom(b []byte) (n int, addr net.Addr, err error) {
	p, ok := <-s.nonUtpReads
	if !ok {
		err = errors.New("closed")
		return
	}
	n = copy(b, p.b)
	addr = p.from
	return
}

func (s *Socket) onReadNonUtp(b []byte, from net.Addr) {
	socketNonUtpPacketsReceived.Add(1)
	select {
	case s.nonUtpReads <- packet{b, from}:
	default:
		log.Printf("dropped non utp packet: no room in buffer")
		nonUtpPacketsDropped.Add(1)
	}
}

func (s *Socket) SetReadDeadline(t time.Time) error {
	return errors.New("not implemented")
}

func (s *Socket) SetWriteDeadline(t time.Time) error {
	return errors.New("not implemented")
}

func (s *Socket) SetDeadline(t time.Time) error {
	return errors.New("not implemented")
}

func (s *Socket) WriteTo(b []byte, addr net.Addr) (int, error) {
	return s.pc.WriteTo(b, addr)
}
<commit_msg>Fix deadlock when accepting a connection that doesn't fit in the backlog<commit_after>package utp

/*
#include "utp.h"
*/
import "C"
import (
	"errors"
	"log"
	"net"
	"time"
)

type Socket struct {
	pc            net.PacketConn
	ctx           *C.utp_context
	backlog       chan *Conn
	closed        bool
	conns         map[*C.utp_socket]*Conn
	nonUtpReads   chan packet
	writeDeadline time.Time
	readDeadline  time.Time
}

var (
	_ net.PacketConn = (*Socket)(nil)
)

type packet struct {
	b    []byte
	from net.Addr
}

func NewSocket(network, addr string) (*Socket, error) {
	pc, err := net.ListenPacket(network, addr)
	if err != nil {
		return nil, err
	}
	mu.Lock()
	defer mu.Unlock()
	ctx := C.utp_init(2)
	if ctx == nil {
		panic(ctx)
	}
	ctx.setCallbacks()
	// ctx.setOption(C.UTP_LOG_NORMAL, 1)
	// ctx.setOption(C.UTP_LOG_MTU, 1)
	// ctx.setOption(C.UTP_LOG_DEBUG, 1)
	s := &Socket{
		pc:          pc,
		ctx:         ctx,
		backlog:     make(chan *Conn, 5),
		conns:       make(map[*C.utp_socket]*Conn),
		nonUtpReads: make(chan packet, 10),
	}
	libContextToSocket[ctx] = s
	go s.packetReader()
	return s, nil
}

func (s *Socket) onLibSocketDestroyed(ls *C.utp_socket) {
	delete(s.conns, ls)
}

func (s *Socket) newConn(us *C.utp_socket) *Conn {
	c := &Conn{
		s: us,
	}
	c.cond.L = &mu
	s.conns[us] = c
	return c
}

func (s *Socket) packetReader() {
	for {
		b := make([]byte, 0x1000)
		n, addr, err := s.pc.ReadFrom(b)
		if err != nil {
			mu.Lock()
			closed := s.closed
			mu.Unlock()
			if closed {
				return
			}
			panic(err)
		}
		sa, sal := netAddrToLibSockaddr(addr)
		func() {
			mu.Lock()
			defer mu.Unlock()
			if s.closed {
				return
			}
			if C.utp_process_udp(s.ctx, (*C.byte)(&b[0]), C.size_t(n), sa, sal) != 0 {
				socketUtpPacketsReceived.Add(1)
			} else {
				s.onReadNonUtp(b[:n], addr)
			}
			C.utp_issue_deferred_acks(s.ctx)
			C.utp_check_timeouts(s.ctx)
		}()
	}
}

func (s *Socket) timeoutChecker() {
	for {
		mu.Lock()
		if s.closed {
			mu.Unlock()
			return
		}
		C.utp_check_timeouts(s.ctx)
		mu.Unlock()
		time.Sleep(500 * time.Millisecond)
	}
}

func (me *Socket) Close() error {
	mu.Lock()
	defer mu.Unlock()
	if me.closed {
		return nil
	}
	C.utp_destroy(me.ctx)
	me.ctx = nil
	me.pc.Close()
	close(me.backlog)
	close(me.nonUtpReads)
	me.closed = true
	return nil
}

func (me *Socket) Addr() net.Addr {
	return me.pc.LocalAddr()
}

func (me *Socket) LocalAddr() net.Addr {
	return me.pc.LocalAddr()
}

func (s *Socket) Accept() (net.Conn, error) {
	nc, ok := <-s.backlog
	if !ok {
		return nil, errors.New("closed")
	}
	return nc, nil
}

func (s *Socket) Dial(addr string) (net.Conn, error) {
	return s.DialTimeout(addr, 0)
}

func (s *Socket) DialTimeout(addr string, timeout time.Duration) (net.Conn, error) {
	ua, err := net.ResolveUDPAddr(s.Addr().Network(), addr)
	if err != nil {
		panic(err)
	}
	sa, sl := netAddrToLibSockaddr(ua)
	mu.Lock()
	c := s.newConn(C.utp_create_socket(s.ctx))
	C.utp_connect(c.s, sa, sl)
	defer mu.Unlock()
	err = c.waitForConnect()
	if err != nil {
		c.close()
		return nil, err
	}
	return c, nil
}

func (s *Socket) pushBacklog(c *Conn) {
	select {
	case s.backlog <- c:
	default:
		c.close()
	}
}

func (s *Socket) ReadFrom(b []byte) (n int, addr net.Addr, err error) {
	p, ok := <-s.nonUtpReads
	if !ok {
		err = errors.New("closed")
		return
	}
	n = copy(b, p.b)
	addr = p.from
	return
}

func (s *Socket) onReadNonUtp(b []byte, from net.Addr) {
	socketNonUtpPacketsReceived.Add(1)
	select {
	case s.nonUtpReads <- packet{b, from}:
	default:
		log.Printf("dropped non utp packet: no room in buffer")
		nonUtpPacketsDropped.Add(1)
	}
}

func (s *Socket) SetReadDeadline(t time.Time) error {
	return errors.New("not implemented")
}

func (s *Socket) SetWriteDeadline(t time.Time) error {
	return errors.New("not implemented")
}

func (s *Socket) SetDeadline(t time.Time) error {
	return errors.New("not implemented")
}

func (s *Socket) WriteTo(b []byte, addr net.Addr) (int, error) {
	return s.pc.WriteTo(b, addr)
}
<|endoftext|>


<commit_before>package utp

/*
#include "utp.h"
*/
import "C"
import (
	"errors"
	"log"
	"net"
	"time"
)

type Socket struct {
	pc            net.PacketConn
	ctx           *C.utp_context
	backlog       chan *Conn
	closed        bool
	conns         map[*C.utp_socket]*Conn
	nonUtpReads   chan packet
	writeDeadline time.Time
	readDeadline  time.Time
}

var (
	_ net.PacketConn = (*Socket)(nil)
)

type packet struct {
	b    []byte
	from net.Addr
}

func NewSocket(network, addr string) (*Socket, error) {
	pc, err := net.ListenPacket(network, addr)
	if err != nil {
		return nil, err
	}
	mu.Lock()
	defer mu.Unlock()
	ctx := C.utp_init(2)
	if ctx == nil {
		panic(ctx)
	}
	ctx.setCallbacks()
	// ctx.setOption(C.UTP_LOG_NORMAL, 1)
	// ctx.setOption(C.UTP_LOG_MTU, 1)
	// ctx.setOption(C.UTP_LOG_DEBUG, 1)
	s := &Socket{
		pc:          pc,
		ctx:         ctx,
		backlog:     make(chan *Conn, 5),
		conns:       make(map[*C.utp_socket]*Conn),
		nonUtpReads: make(chan packet, 10),
	}
	libContextToSocket[ctx] = s
	go s.packetReader()
	return s, nil
}

func (s *Socket) onLibSocketDestroyed(ls *C.utp_socket) {
	delete(s.conns, ls)
}

func (s *Socket) newConn(us *C.utp_socket) *Conn {
	c := &Conn{
		s: us,
	}
	c.cond.L = &mu
	s.conns[us] = c
	return c
}

func (s *Socket) packetReader() {
	for {
		b := make([]byte, 0x1000)
		n, addr, err := s.pc.ReadFrom(b)
		if err != nil {
			mu.Lock()
			closed := s.closed
			mu.Unlock()
			if closed {
				return
			}
			panic(err)
		}
		sa, sal := netAddrToLibSockaddr(addr)
		func() {
			mu.Lock()
			defer mu.Unlock()
			if s.closed {
				return
			}
			if C.utp_process_udp(s.ctx, (*C.byte)(&b[0]), C.size_t(n), sa, sal) == 0 {
				s.onReadNonUtp(b[:n], addr)
			}
			C.utp_issue_deferred_acks(s.ctx)
			C.utp_check_timeouts(s.ctx)
		}()
	}
}

func (s *Socket) timeoutChecker() {
	for {
		mu.Lock()
		if s.closed {
			mu.Unlock()
			return
		}
		C.utp_check_timeouts(s.ctx)
		mu.Unlock()
		time.Sleep(500 * time.Millisecond)
	}
}

func (me *Socket) Close() error {
	mu.Lock()
	defer mu.Unlock()
	if me.closed {
		return nil
	}
	C.utp_destroy(me.ctx)
	me.ctx = nil
	me.pc.Close()
	close(me.backlog)
	me.closed = true
	return nil
}

func (me *Socket) Addr() net.Addr {
	return me.pc.LocalAddr()
}

func (me *Socket) LocalAddr() net.Addr {
	return me.pc.LocalAddr()
}

func (s *Socket) Accept() (net.Conn, error) {
	nc, ok := <-s.backlog
	if !ok {
		return nil, errors.New("closed")
	}
	return nc, nil
}

func (s *Socket) Dial(addr string) (net.Conn, error) {
	return s.DialTimeout(addr, 0)
}

func (s *Socket) DialTimeout(addr string, timeout time.Duration) (net.Conn, error) {
	ua, err := net.ResolveUDPAddr(s.Addr().Network(), addr)
	if err != nil {
		panic(err)
	}
	sa, sl := netAddrToLibSockaddr(ua)
	mu.Lock()
	c := s.newConn(C.utp_create_socket(s.ctx))
	C.utp_connect(c.s, sa, sl)
	defer mu.Unlock()
	err = c.waitForConnect()
	if err != nil {
		c.close()
		return nil, err
	}
	return c, nil
}

func (s *Socket) pushBacklog(c *Conn) {
	select {
	case s.backlog <- c:
	default:
		c.Close()
	}
}

func (s *Socket) ReadFrom(b []byte) (n int, addr net.Addr, err error) {
	p := <-s.nonUtpReads
	n = copy(b, p.b)
	addr = p.from
	return
}

func (s *Socket) onReadNonUtp(b []byte, from net.Addr) {
	select {
	case s.nonUtpReads <- packet{b, from}:
	default:
		log.Printf("dropped non utp packet: no room in buffer")
	}
}

func (s *Socket) SetReadDeadline(t time.Time) error {
	return errors.New("not implemented")
}

func (s *Socket) SetWriteDeadline(t time.Time) error {
	return errors.New("not implemented")
}

func (s *Socket) SetDeadline(t time.Time) error {
	return errors.New("not implemented")
}

func (s *Socket) WriteTo(b []byte, addr net.Addr) (int, error) {
	return s.pc.WriteTo(b, addr)
}
<commit_msg>Apply Socket.Close to non uTP PacketConn functions<commit_after>package utp

/*
#include "utp.h"
*/
import "C"
import (
	"errors"
	"log"
	"net"
	"time"
)

type Socket struct {
	pc            net.PacketConn
	ctx           *C.utp_context
	backlog       chan *Conn
	closed        bool
	conns         map[*C.utp_socket]*Conn
	nonUtpReads   chan packet
	writeDeadline time.Time
	readDeadline  time.Time
}

var (
	_ net.PacketConn = (*Socket)(nil)
)

type packet struct {
	b    []byte
	from net.Addr
}

func NewSocket(network, addr string) (*Socket, error) {
	pc, err := net.ListenPacket(network, addr)
	if err != nil {
		return nil, err
	}
	mu.Lock()
	defer mu.Unlock()
	ctx := C.utp_init(2)
	if ctx == nil {
		panic(ctx)
	}
	ctx.setCallbacks()
	// ctx.setOption(C.UTP_LOG_NORMAL, 1)
	// ctx.setOption(C.UTP_LOG_MTU, 1)
	// ctx.setOption(C.UTP_LOG_DEBUG, 1)
	s := &Socket{
		pc:          pc,
		ctx:         ctx,
		backlog:     make(chan *Conn, 5),
		conns:       make(map[*C.utp_socket]*Conn),
		nonUtpReads: make(chan packet, 10),
	}
	libContextToSocket[ctx] = s
	go s.packetReader()
	return s, nil
}

func (s *Socket) onLibSocketDestroyed(ls *C.utp_socket) {
	delete(s.conns, ls)
}

func (s *Socket) newConn(us *C.utp_socket) *Conn {
	c := &Conn{
		s: us,
	}
	c.cond.L = &mu
	s.conns[us] = c
	return c
}

func (s *Socket) packetReader() {
	for {
		b := make([]byte, 0x1000)
		n, addr, err := s.pc.ReadFrom(b)
		if err != nil {
			mu.Lock()
			closed := s.closed
			mu.Unlock()
			if closed {
				return
			}
			panic(err)
		}
		sa, sal := netAddrToLibSockaddr(addr)
		func() {
			mu.Lock()
			defer mu.Unlock()
			if s.closed {
				return
			}
			if C.utp_process_udp(s.ctx, (*C.byte)(&b[0]), C.size_t(n), sa, sal) == 0 {
				s.onReadNonUtp(b[:n], addr)
			}
			C.utp_issue_deferred_acks(s.ctx)
			C.utp_check_timeouts(s.ctx)
		}()
	}
}

func (s *Socket) timeoutChecker() {
	for {
		mu.Lock()
		if s.closed {
			mu.Unlock()
			return
		}
		C.utp_check_timeouts(s.ctx)
		mu.Unlock()
		time.Sleep(500 * time.Millisecond)
	}
}

func (me *Socket) Close() error {
	mu.Lock()
	defer mu.Unlock()
	if me.closed {
		return nil
	}
	C.utp_destroy(me.ctx)
	me.ctx = nil
	me.pc.Close()
	close(me.backlog)
	close(me.nonUtpReads)
	me.closed = true
	return nil
}

func (me *Socket) Addr() net.Addr {
	return me.pc.LocalAddr()
}

func (me *Socket) LocalAddr() net.Addr {
	return me.pc.LocalAddr()
}

func (s *Socket) Accept() (net.Conn, error) {
	nc, ok := <-s.backlog
	if !ok {
		return nil, errors.New("closed")
	}
	return nc, nil
}

func (s *Socket) Dial(addr string) (net.Conn, error) {
	return s.DialTimeout(addr, 0)
}

func (s *Socket) DialTimeout(addr string, timeout time.Duration) (net.Conn, error) {
	ua, err := net.ResolveUDPAddr(s.Addr().Network(), addr)
	if err != nil {
		panic(err)
	}
	sa, sl := netAddrToLibSockaddr(ua)
	mu.Lock()
	c := s.newConn(C.utp_create_socket(s.ctx))
	C.utp_connect(c.s, sa, sl)
	defer mu.Unlock()
	err = c.waitForConnect()
	if err != nil {
		c.close()
		return nil, err
	}
	return c, nil
}

func (s *Socket) pushBacklog(c *Conn) {
	select {
	case s.backlog <- c:
	default:
		c.Close()
	}
}

func (s *Socket) ReadFrom(b []byte) (n int, addr net.Addr, err error) {
	p, ok := <-s.nonUtpReads
	if !ok {
		err = errors.New("closed")
		return
	}
	n = copy(b, p.b)
	addr = p.from
	return
}

func (s *Socket) onReadNonUtp(b []byte, from net.Addr) {
	select {
	case s.nonUtpReads <- packet{b, from}:
	default:
		log.Printf("dropped non utp packet: no room in buffer")
	}
}

func (s *Socket) SetReadDeadline(t time.Time) error {
	return errors.New("not implemented")
}

func (s *Socket) SetWriteDeadline(t time.Time) error {
	return errors.New("not implemented")
}

func (s *Socket) SetDeadline(t time.Time) error {
	return errors.New("not implemented")
}

func (s *Socket) WriteTo(b []byte, addr net.Addr) (int, error) {
	return s.pc.WriteTo(b, addr)
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"flag"
	"fmt"
	"github.com/lzcqd/sedgewick/chap2_sorting/insertionsort"
	"github.com/lzcqd/sedgewick/chap2_sorting/mergesort"
	"github.com/lzcqd/sedgewick/chap2_sorting/selectionsort"
	"github.com/lzcqd/sedgewick/chap2_sorting/shellsort"
	"github.com/lzcqd/sedgewick/chap2_sorting/sortable"
	"math/rand"
	"reflect"
	"runtime"
	"strings"
	"sync"
	"time"
)

func timesort(sort func(sortable.Interface), to_sort []sortable.Floatslice, out chan string) {
	defer close(out)

	start := time.Now()
	for _, s := range to_sort {
		sort(s)
	}

	duration := time.Since(start)
	out <- fmt.Sprintf("%s completed in %v", runtime.FuncForPC(reflect.ValueOf(sort).Pointer()).Name(), duration)
}

func merge(cs ...chan string) <-chan string {
	var wg sync.WaitGroup
	out := make(chan string)

	output := func(c <-chan string) {
		for n := range c {
			out <- n
		}
		wg.Done()
	}

	wg.Add(len(cs))
	for _, c := range cs {
		go output(c)
	}

	go func() {
		wg.Wait()
		close(out)
	}()

	return out
}

func startsorts(sorts []func(sortable.Interface), to_sort []sortable.Floatslice, timeout int) {
	outs := make([]chan string, len(sorts))
	for i := range outs {
		outs[i] = make(chan string)
	}

	out := merge(outs...)
	done := make(chan bool)
	go manageOutput(out, timeout, done)

	for i, s := range sorts {
		new_sort := make([]sortable.Floatslice, len(to_sort))
		for j := range new_sort {
			var fs []float64
			fs = append(fs, to_sort[j]...)
			new_sort[j] = fs
		}
		go timesort(s, new_sort, outs[i])
	}
	fmt.Println("sorting...")
	<-done
}

func manageOutput(out <-chan string, timeout int, done chan bool) {
	defer func() {
		done <- true
	}()

	quit := time.Tick(time.Duration(timeout) * time.Second)

	for {
		select {
		case s, ok := <-out:
			fmt.Println(s)
			if !ok {
				return
			}
		case <-quit:
			fmt.Println("Time out.")
			return
		}
	}
}

func getSortFunc(in string) (func(sortable.Interface), error) {
	switch in {
	case "selectionsort":
		return selectionsort.Sort, nil
	case "insertionsort":
		return insertionsort.Sort, nil
	case "shellsort":
		return shellsort.Sort, nil
	case "mergesort":
		return mergesort.Sort, nil
	case "mergesortBU":
		return mergesort.SortBU, nil
	default:
		return nil, errors.New("fail to parse sort function")
	}
}

func generateSortArray(arrayCount, elementCount int) []sortable.Floatslice {
	ret := make([]sortable.Floatslice, arrayCount)
	for i := range ret {
		array := make([]float64, elementCount)
		rand.Seed(int64(time.Now().UnixNano()))
		for j := range array {
			array[j] = rand.Float64()
		}
		ret[i] = array
	}
	return ret
}

func main() {
	sortFuncs := flag.String("sorts", "", "Sorting functions to compare, comma separated")
	arrayCount := flag.Int("array", 1, "Number of arrays to sort, default 1")
	elementCount := flag.Int("element", 1000, "Number of random entries for each array, default 1000")
	timeout := flag.Int("timeout", 60, "Maximum time to run in seconds, default 60s")
	flag.Parse()

	funcs := strings.Split(*sortFuncs, ",")
	for i, f := range funcs {
		funcs[i] = strings.TrimSpace(f)
	}

	var sorts []func(sortable.Interface)

	for _, f := range funcs {
		s, err := getSortFunc(f)
		if err != nil {
			panic(fmt.Sprintf("Not recognised sort function: %s\n", s))
		}
		sorts = append(sorts, s)
	}

	sortArray := generateSortArray(*arrayCount, *elementCount)
	startsorts(sorts, sortArray, *timeout)
}
<commit_msg>Include quicksort into sortcompare<commit_after>package main

import (
	"errors"
	"flag"
	"fmt"
	"github.com/lzcqd/sedgewick/chap2_sorting/insertionsort"
	"github.com/lzcqd/sedgewick/chap2_sorting/mergesort"
	"github.com/lzcqd/sedgewick/chap2_sorting/quicksort"
	"github.com/lzcqd/sedgewick/chap2_sorting/selectionsort"
	"github.com/lzcqd/sedgewick/chap2_sorting/shellsort"
	"github.com/lzcqd/sedgewick/chap2_sorting/sortable"
	"math/rand"
	"reflect"
	"runtime"
	"strings"
	"sync"
	"time"
)

func timesort(sort func(sortable.Interface), to_sort []sortable.Floatslice, out chan string) {
	defer close(out)

	start := time.Now()
	for _, s := range to_sort {
		sort(s)
	}

	duration := time.Since(start)
	out <- fmt.Sprintf("%s completed in %v", runtime.FuncForPC(reflect.ValueOf(sort).Pointer()).Name(), duration)
}

func merge(cs ...chan string) <-chan string {
	var wg sync.WaitGroup
	out := make(chan string)

	output := func(c <-chan string) {
		for n := range c {
			out <- n
		}
		wg.Done()
	}

	wg.Add(len(cs))
	for _, c := range cs {
		go output(c)
	}

	go func() {
		wg.Wait()
		close(out)
	}()

	return out
}

func startsorts(sorts []func(sortable.Interface), to_sort []sortable.Floatslice, timeout int) {
	outs := make([]chan string, len(sorts))
	for i := range outs {
		outs[i] = make(chan string)
	}

	out := merge(outs...)
	done := make(chan bool)
	go manageOutput(out, timeout, done)

	for i, s := range sorts {
		new_sort := make([]sortable.Floatslice, len(to_sort))
		for j := range new_sort {
			var fs []float64
			fs = append(fs, to_sort[j]...)
			new_sort[j] = fs
		}
		go timesort(s, new_sort, outs[i])
	}
	fmt.Println("sorting...")
	<-done
}

func manageOutput(out <-chan string, timeout int, done chan bool) {
	defer func() {
		done <- true
	}()

	quit := time.Tick(time.Duration(timeout) * time.Second)

	for {
		select {
		case s, ok := <-out:
			fmt.Println(s)
			if !ok {
				return
			}
		case <-quit:
			fmt.Println("Time out.")
			return
		}
	}
}

func getSortFunc(in string) (func(sortable.Interface), error) {
	switch in {
	case "selectionsort":
		return selectionsort.Sort, nil
	case "insertionsort":
		return insertionsort.Sort, nil
	case "shellsort":
		return shellsort.Sort, nil
	case "mergesort":
		return mergesort.Sort, nil
	case "mergesortBU":
		return mergesort.SortBU, nil
	case "quicksort":
		return quicksort.Sort, nil
	default:
		return nil, errors.New("fail to parse sort function")
	}
}

func generateSortArray(arrayCount, elementCount int) []sortable.Floatslice {
	ret := make([]sortable.Floatslice, arrayCount)
	for i := range ret {
		array := make([]float64, elementCount)
		rand.Seed(int64(time.Now().UnixNano()))
		for j := range array {
			array[j] = rand.Float64()
		}
		ret[i] = array
	}
	return ret
}

func main() {
	sortFuncs := flag.String("sorts", "", "Sorting functions to compare, comma separated")
	arrayCount := flag.Int("array", 1, "Number of arrays to sort, default 1")
	elementCount := flag.Int("element", 1000, "Number of random entries for each array, default 1000")
	timeout := flag.Int("timeout", 60, "Maximum time to run in seconds, default 60s")
	flag.Parse()

	funcs := strings.Split(*sortFuncs, ",")
	for i, f := range funcs {
		funcs[i] = strings.TrimSpace(f)
	}

	var sorts []func(sortable.Interface)

	for _, f := range funcs {
		s, err := getSortFunc(f)
		if err != nil {
			panic(fmt.Sprintf("Not recognised sort function: %s\n", s))
		}
		sorts = append(sorts, s)
	}

	sortArray := generateSortArray(*arrayCount, *elementCount)
	startsorts(sorts, sortArray, *timeout)
}
<|endoftext|>


<commit_before>package wbrules

import (
	"fmt"
	"github.com/contactless/wbgo"
	"github.com/contactless/wbgo/testutils"
	"io/ioutil"
	"os"
	"testing"
)

type RuleReloadSuite struct {
	RuleSuiteBase

	reloadTmpDir string
}

func (s *RuleReloadSuite) SetupTest() {
	var err error
	s.reloadTmpDir, err = ioutil.TempDir(os.TempDir(), "wbrulestest")
	if err != nil {
		s.FailNow("can't create temp directory")
	}
	wbgo.Debug.Printf("created temp dir %s for reload tests", s.reloadTmpDir)

	s.VdevStorageFile = s.reloadTmpDir + "/test_vdev.db"
	s.PersistentDBFile = s.reloadTmpDir + "/test_persistent.db"

	s.SetupSkippingDefs("testrules_reload_1.js", "testrules_reload_2.js")
	s.Verify(
		"[info] detRun",
		"[info] detectRun: somedev/temp (s=false, a=10)",
		"[info] detectRun1: somedev/temp (s=false, a=10)",
	)
}

func (s *RuleReloadSuite) VerifyVdevCleanup(file string) {
	if file == "testrules_reload_2.js" {
		s.VerifyUnordered(
			// devices are removed
			"Unsubscribe -- driver: /devices/vdev/controls/someCell/on",
			"Unsubscribe -- driver: /devices/vdev/controls/anotherCell/on",
			"driver -> /devices/vdev/meta/name: [] (QoS 1, retained)",
			"driver -> /devices/vdev/controls/anotherCell/meta/type: [] (QoS 1, retained)",
			"driver -> /devices/vdev/controls/anotherCell/meta/writable: [] (QoS 1, retained)",
			"driver -> /devices/vdev/controls/anotherCell/meta/readonly: [] (QoS 1, retained)",
			"driver -> /devices/vdev/controls/anotherCell/meta/order: [] (QoS 1, retained)",
			"driver -> /devices/vdev/controls/anotherCell/meta/max: [] (QoS 1, retained)",
			"driver -> /devices/vdev/controls/anotherCell: [] (QoS 1, retained)",
			"driver -> /devices/vdev/controls/someCell/meta/type: [] (QoS 1, retained)",
			"driver -> /devices/vdev/controls/someCell/meta/writable: [] (QoS 1, retained)",
			"driver -> /devices/vdev/controls/someCell/meta/readonly: [] (QoS 1, retained)",
			"driver -> /devices/vdev/controls/someCell/meta/order: [] (QoS 1, retained)",
			"driver -> /devices/vdev/controls/someCell: [] (QoS 1, retained)",
			"driver -> /devices/vdev/meta/driver: [] (QoS 1, retained)",

			"Unsubscribe -- driver: /devices/vdev1/controls/qqq/on",
			"driver -> /devices/vdev1/meta/name: [] (QoS 1, retained)",
			"driver -> /devices/vdev1/meta/driver: [] (QoS 1, retained)",
			"driver -> /devices/vdev1/controls/qqq/meta/type: [] (QoS 1, retained)",
			"driver -> /devices/vdev1/controls/qqq/meta/writable: [] (QoS 1, retained)",
			"driver -> /devices/vdev1/controls/qqq/meta/readonly: [] (QoS 1, retained)",
			"driver -> /devices/vdev1/controls/qqq/meta/order: [] (QoS 1, retained)",
			"driver -> /devices/vdev1/controls/qqq: [] (QoS 1, retained)",
		)
	}
}

func (s *RuleReloadSuite) VerifyRules() {
	s.publish("/devices/vdev/controls/someCell/on", "1", "vdev/someCell")
	s.VerifyUnordered(
		"tst -> /devices/vdev/controls/someCell/on: [1] (QoS 1)",
		"driver -> /devices/vdev/controls/someCell: [1] (QoS 1, retained)",
		"[info] detRun",
		"[info] detectRun: vdev/someCell (s=true, a=10)",
		"[info] detectRun1: vdev/someCell (s=true, a=10)",
		"[info] rule1: vdev/someCell=true",
		"[info] rule2: vdev/someCell=true",
	)

	s.publish("/devices/vdev/controls/anotherCell/on", "17", "vdev/anotherCell")
	s.VerifyUnordered(
		"tst -> /devices/vdev/controls/anotherCell/on: [17] (QoS 1)",
		"driver -> /devices/vdev/controls/anotherCell: [17] (QoS 1, retained)",
		"[info] detRun",
		"[info] detectRun: vdev/anotherCell (s=true, a=17)",
		"[info] detectRun1: vdev/anotherCell (s=true, a=17)",
		"[info] rule3: vdev/anotherCell=17",
	)
}

func (s *RuleReloadSuite) TestReload() {

	s.VerifyRules()

	s.ReplaceScript("testrules_reload_2.js", "testrules_reload_2_changed.js")
	s.VerifyVdevCleanup("testrules_reload_2.js")
	s.VerifyUnordered(
		// device redefinition begins
		"driver -> /devices/vdev/meta/name: [VDev] (QoS 1, retained)",
		"driver -> /devices/vdev/meta/driver: [wbrules] (QoS 1, retained)",
		"driver -> /devices/vdev/controls/someCell/meta/type: [switch] (QoS 1, retained)",
		"driver -> /devices/vdev/controls/someCell/meta/writable: [1] (QoS 1, retained)",
		"driver -> /devices/vdev/controls/someCell/meta/readonly: [] (QoS 1, retained)",
		"driver -> /devices/vdev/controls/someCell/meta/order: [1] (QoS 1, retained)",
		// value '1' of the switch from the retained message
		"driver -> /devices/vdev/controls/someCell: [1] (QoS 1, retained)",
		"Subscribe -- driver: /devices/vdev/controls/someCell/on",
	)
	// rules are run after reload
	// "[debug] defineRule: detectRun",
	// "[debug] defineRule: rule1",
	// "[info] detRun",
	// "[info] detectRun: (no cell) (s=true)",
	// change notification for the client-side script editor
	s.SkipTill("[changed] testrules_reload_2.js")

	// this one must be ignored because anotherCell is no longer there
	// after the device is redefined
	s.publish("/devices/vdev/controls/anotherCell/on", "11")
	s.publish("/devices/vdev/controls/someCell/on", "0", "vdev/someCell")

	s.Verify(
		"tst -> /devices/vdev/controls/anotherCell/on: [11] (QoS 1)",
		"tst -> /devices/vdev/controls/someCell/on: [0] (QoS 1)",
		"driver -> /devices/vdev/controls/someCell: [0] (QoS 1, retained)",
		"[info] detRun",
		"[info] detectRun: vdev/someCell (s=false)",
		"[info] rule1: vdev/someCell=false",
		// rule2 is gone, rule3 is gone together with its anotherCell
	)

	s.publish("/devices/vdev/controls/someCell/on", "1", "vdev/someCell")
	s.Verify(
		"tst -> /devices/vdev/controls/someCell/on: [1] (QoS 1)",
		"driver -> /devices/vdev/controls/someCell: [1] (QoS 1, retained)",
		"[info] detRun",
		"[info] detectRun: vdev/someCell (s=true)",
		"[info] rule1: vdev/someCell=true",
	)

	// TBD: stop any timers started while evaluating the script.
	// This will require extra care because cleanup procedure
	// for the timer must be revoked once the timer is stopped.
}

func (s *RuleReloadSuite) TestRemoveScript() {
	s.RemoveScript("testrules_reload_2.js")
	s.VerifyVdevCleanup("testrules_reload_2.js")
	s.Verify(
		// removal notification for the client-side script editor
		"[removed] testrules_reload_2.js",
	)

	// both ignored (cells are no longer there)
	s.publish("/devices/vdev/controls/anotherCell/on", "11")
	s.publish("/devices/vdev/controls/someCell/on", "0")

	s.Verify(
		"tst -> /devices/vdev/controls/anotherCell/on: [11] (QoS 1)",
		"tst -> /devices/vdev/controls/someCell/on: [0] (QoS 1)",
	)

	// vdev0 is intact because it's from testrules_reload_1.js
	s.publish("/devices/vdev0/controls/someCell/on", "1", "vdev0/someCell")
	s.Verify(
		"tst -> /devices/vdev0/controls/someCell/on: [1] (QoS 1)",
		"driver -> /devices/vdev0/controls/someCell: [1] (QoS 1, retained)",
		"[info] detRun",
	)
}

func (s *RuleReloadSuite) TestIndirectRulesCleanup() {
	// advance time to define rule in timeout
	s.FireTimer(1, s.CurrentTime())
	s.Verify(
		"timer.fire(): 1",
		"[info] timeout set",
	)

	// check indirect rule run
	s.publish("/devices/vdev1/controls/qqq/on", "1", "vdev1/qqq")
	s.Verify(
		"tst -> /devices/vdev1/controls/qqq/on: [1] (QoS 1)",
		"driver -> /devices/vdev1/controls/qqq: [1] (QoS 1, retained)",
	)
	s.VerifyUnordered(
		"[info] detRun",
		"[info] checkIndirect",
		"[info] detectRun1: vdev1/qqq (s=false, a=10)",
		"[info] detectRun: vdev1/qqq (s=false, a=10)",
	)

	// remove script
	s.RemoveScript("testrules_reload_1.js")
	s.SkipTill("[removed] testrules_reload_1.js")

	// check rule again
	s.publish("/devices/vdev1/controls/qqq/on", "1", "vdev1/qqq")
	s.Verify(
		"tst -> /devices/vdev1/controls/qqq/on: [1] (QoS 1)",
		"driver -> /devices/vdev1/controls/qqq: [1] (QoS 1, retained)",
	)
	s.VerifyUnordered(
		"[info] detectRun1: vdev1/qqq (s=false, a=10)",
		"[info] detectRun: vdev1/qqq (s=false, a=10)",
	)
	s.VerifyEmpty()
}

func (s *RuleReloadSuite) TestRemoveRestore() {
	s.RemoveScript("testrules_reload_2.js")
	s.VerifyVdevCleanup("testrules_reload_2.js")
	s.Verify(
		// removal notification for the client-side script editor
		"[removed] testrules_reload_2.js",
	)

	// load script and expect vdev definition at least
	s.LiveLoadScript("testrules_reload_2.js")
	// s.ReplaceScript("testrules_reload_2.js", "testrules_reload_2_changed.js")
	s.SkipTill("[changed] testrules_reload_2.js")

	s.VerifyRules()
}

func (s *RuleReloadSuite) TestNoReloading() {
	s.engine.EvalScript("testrules_reload_1_loaded = false;")
	// no actual replacement should happen here
	s.ReplaceScript("testrules_reload_1.js", "testrules_reload_1.js")
	s.Ck("unexpected reload", s.engine.EvalScript(
		"if(testrules_reload_1_loaded) throw new Error('unexpected reload')"))
	s.VerifyEmpty()
}

func (s *RuleReloadSuite) verifyReloadCount(n int) {
	script := fmt.Sprintf(
		`if(testrules_reload_2_n != %d)
                   throw new Error(
                     "bad reload count: " + testrules_reload_2_n + " instead of " + %d)`,
		n, n)
	s.Ck("bad reload count", s.engine.EvalScript(script))
}

func (s *RuleReloadSuite) TestOverwriteScript() {
	// let's load script from a new directory
	s.Ck("OverwriteScript()",
		s.OverwriteScript("subdir/testrules_reload_42.js", "testrules_reload_2_changed.js"))
	s.Verify("[info] Error: Device with given ID already exists")

	s.EnsureGotErrors() // got warning for vdev redefinition
	s.verifyReloadCount(1)
	s.SkipTill("[changed] subdir/testrules_reload_42.js")

	// the following ReplaceScript() which calls LiveLoadFile()
	// has now effect because the new content is already registered
	s.ReplaceScript("testrules_reload_2.js", "testrules_reload_2_changed.js")
	s.verifyReloadCount(2)
	s.SkipTill("[changed] testrules_reload_2.js")
}

func (s *RuleReloadSuite) TestWriteScript() {
	for n := 1; n < 3; n++ {
		// OverwriteScript() calls LiveWriteScript() which causes
		// script to reload every time
		s.Ck("OverwriteScript()",
			s.OverwriteScript("testrules_reload_2.js", "testrules_reload_2_changed.js"))
		s.verifyReloadCount(n)
		s.SkipTill("[changed] testrules_reload_2.js")
	}
}

func TestRuleReloadSuite(t *testing.T) {
	testutils.RunSuites(t,
		new(RuleReloadSuite),
	)
}
<commit_msg>test: fix race in rule reload tests<commit_after>package wbrules

import (
	"fmt"
	"github.com/contactless/wbgo"
	"github.com/contactless/wbgo/testutils"
	"io/ioutil"
	"os"
	"testing"
)

type RuleReloadSuite struct {
	RuleSuiteBase

	reloadTmpDir string
}

func (s *RuleReloadSuite) SetupTest() {
	var err error
	s.reloadTmpDir, err = ioutil.TempDir(os.TempDir(), "wbrulestest")
	if err != nil {
		s.FailNow("can't create temp directory")
	}
	wbgo.Debug.Printf("created temp dir %s for reload tests", s.reloadTmpDir)

	s.VdevStorageFile = s.reloadTmpDir + "/test_vdev.db"
	s.PersistentDBFile = s.reloadTmpDir + "/test_persistent.db"

	s.SetupSkippingDefs("testrules_reload_1.js", "testrules_reload_2.js")
	s.Verify(
		"[info] detRun",
		"[info] detectRun: somedev/temp (s=false, a=10)",
		"[info] detectRun1: somedev/temp (s=false, a=10)",
	)
}

func (s *RuleReloadSuite) VerifyVdevCleanup(file string) {
	if file == "testrules_reload_2.js" {
		s.VerifyUnordered(
			// devices are removed
			"Unsubscribe -- driver: /devices/vdev/controls/someCell/on",
			"Unsubscribe -- driver: /devices/vdev/controls/anotherCell/on",
			"driver -> /devices/vdev/meta/name: [] (QoS 1, retained)",
			"driver -> /devices/vdev/controls/anotherCell/meta/type: [] (QoS 1, retained)",
			"driver -> /devices/vdev/controls/anotherCell/meta/writable: [] (QoS 1, retained)",
			"driver -> /devices/vdev/controls/anotherCell/meta/readonly: [] (QoS 1, retained)",
			"driver -> /devices/vdev/controls/anotherCell/meta/order: [] (QoS 1, retained)",
			"driver -> /devices/vdev/controls/anotherCell/meta/max: [] (QoS 1, retained)",
			"driver -> /devices/vdev/controls/anotherCell: [] (QoS 1, retained)",
			"driver -> /devices/vdev/controls/someCell/meta/type: [] (QoS 1, retained)",
			"driver -> /devices/vdev/controls/someCell/meta/writable: [] (QoS 1, retained)",
			"driver -> /devices/vdev/controls/someCell/meta/readonly: [] (QoS 1, retained)",
			"driver -> /devices/vdev/controls/someCell/meta/order: [] (QoS 1, retained)",
			"driver -> /devices/vdev/controls/someCell: [] (QoS 1, retained)",
			"driver -> /devices/vdev/meta/driver: [] (QoS 1, retained)",

			"Unsubscribe -- driver: /devices/vdev1/controls/qqq/on",
			"driver -> /devices/vdev1/meta/name: [] (QoS 1, retained)",
			"driver -> /devices/vdev1/meta/driver: [] (QoS 1, retained)",
			"driver -> /devices/vdev1/controls/qqq/meta/type: [] (QoS 1, retained)",
			"driver -> /devices/vdev1/controls/qqq/meta/writable: [] (QoS 1, retained)",
			"driver -> /devices/vdev1/controls/qqq/meta/readonly: [] (QoS 1, retained)",
			"driver -> /devices/vdev1/controls/qqq/meta/order: [] (QoS 1, retained)",
			"driver -> /devices/vdev1/controls/qqq: [] (QoS 1, retained)",
		)
	}
}

func (s *RuleReloadSuite) VerifyRules() {
	s.publish("/devices/vdev/controls/someCell/on", "1", "vdev/someCell")
	s.VerifyUnordered(
		"tst -> /devices/vdev/controls/someCell/on: [1] (QoS 1)",
		"driver -> /devices/vdev/controls/someCell: [1] (QoS 1, retained)",
		"[info] detRun",
		"[info] detectRun: vdev/someCell (s=true, a=10)",
		"[info] detectRun1: vdev/someCell (s=true, a=10)",
		"[info] rule1: vdev/someCell=true",
		"[info] rule2: vdev/someCell=true",
	)

	s.publish("/devices/vdev/controls/anotherCell/on", "17", "vdev/anotherCell")
	s.VerifyUnordered(
		"tst -> /devices/vdev/controls/anotherCell/on: [17] (QoS 1)",
		"driver -> /devices/vdev/controls/anotherCell: [17] (QoS 1, retained)",
		"[info] detRun",
		"[info] detectRun: vdev/anotherCell (s=true, a=17)",
		"[info] detectRun1: vdev/anotherCell (s=true, a=17)",
		"[info] rule3: vdev/anotherCell=17",
	)
}

func (s *RuleReloadSuite) TestReload() {

	s.VerifyRules()

	s.ReplaceScript("testrules_reload_2.js", "testrules_reload_2_changed.js")
	s.VerifyVdevCleanup("testrules_reload_2.js")
	s.VerifyUnordered(
		// device redefinition begins
		"driver -> /devices/vdev/meta/name: [VDev] (QoS 1, retained)",
		"driver -> /devices/vdev/meta/driver: [wbrules] (QoS 1, retained)",
		"driver -> /devices/vdev/controls/someCell/meta/type: [switch] (QoS 1, retained)",
		"driver -> /devices/vdev/controls/someCell/meta/writable: [1] (QoS 1, retained)",
		"driver -> /devices/vdev/controls/someCell/meta/readonly: [] (QoS 1, retained)",
		"driver -> /devices/vdev/controls/someCell/meta/order: [1] (QoS 1, retained)",
		// value '1' of the switch from the retained message
		"driver -> /devices/vdev/controls/someCell: [1] (QoS 1, retained)",
		"Subscribe -- driver: /devices/vdev/controls/someCell/on",
	)
	// rules are run after reload
	// "[debug] defineRule: detectRun",
	// "[debug] defineRule: rule1",
	// "[info] detRun",
	// "[info] detectRun: (no cell) (s=true)",
	// change notification for the client-side script editor
	s.SkipTill("[changed] testrules_reload_2.js")

	// this one must be ignored because anotherCell is no longer there
	// after the device is redefined
	s.publish("/devices/vdev/controls/anotherCell/on", "11")
	s.publish("/devices/vdev/controls/someCell/on", "0", "vdev/someCell")

	s.Verify(
		"tst -> /devices/vdev/controls/anotherCell/on: [11] (QoS 1)",
		"tst -> /devices/vdev/controls/someCell/on: [0] (QoS 1)",
		"driver -> /devices/vdev/controls/someCell: [0] (QoS 1, retained)",
		"[info] detRun",
		"[info] detectRun: vdev/someCell (s=false)",
		"[info] rule1: vdev/someCell=false",
		// rule2 is gone, rule3 is gone together with its anotherCell
	)

	s.publish("/devices/vdev/controls/someCell/on", "1", "vdev/someCell")
	s.Verify(
		"tst -> /devices/vdev/controls/someCell/on: [1] (QoS 1)",
		"driver -> /devices/vdev/controls/someCell: [1] (QoS 1, retained)",
		"[info] detRun",
		"[info] detectRun: vdev/someCell (s=true)",
		"[info] rule1: vdev/someCell=true",
	)

	// TBD: stop any timers started while evaluating the script.
	// This will require extra care because cleanup procedure
	// for the timer must be revoked once the timer is stopped.
}

func (s *RuleReloadSuite) TestRemoveScript() {
	s.RemoveScript("testrules_reload_2.js")
	s.VerifyVdevCleanup("testrules_reload_2.js")
	s.Verify(
		// removal notification for the client-side script editor
		"[removed] testrules_reload_2.js",
	)

	// both ignored (cells are no longer there)
	s.publish("/devices/vdev/controls/anotherCell/on", "11")
	s.publish("/devices/vdev/controls/someCell/on", "0")

	s.Verify(
		"tst -> /devices/vdev/controls/anotherCell/on: [11] (QoS 1)",
		"tst -> /devices/vdev/controls/someCell/on: [0] (QoS 1)",
	)

	// vdev0 is intact because it's from testrules_reload_1.js
	s.publish("/devices/vdev0/controls/someCell/on", "1", "vdev0/someCell")
	s.Verify(
		"tst -> /devices/vdev0/controls/someCell/on: [1] (QoS 1)",
		"driver -> /devices/vdev0/controls/someCell: [1] (QoS 1, retained)",
		"[info] detRun",
	)
}

func (s *RuleReloadSuite) TestIndirectRulesCleanup() {
	// advance time to define rule in timeout
	s.FireTimer(1, s.CurrentTime())
	s.Verify(
		"timer.fire(): 1",
		"[info] timeout set",
	)

	// check indirect rule run
	s.publish("/devices/vdev1/controls/qqq/on", "1", "vdev1/qqq")
	s.VerifyUnordered(
		"tst -> /devices/vdev1/controls/qqq/on: [1] (QoS 1)",
		"driver -> /devices/vdev1/controls/qqq: [1] (QoS 1, retained)",
		"[info] detRun",
		"[info] checkIndirect",
		"[info] detectRun1: vdev1/qqq (s=false, a=10)",
		"[info] detectRun: vdev1/qqq (s=false, a=10)",
	)

	// remove script
	s.RemoveScript("testrules_reload_1.js")
	s.SkipTill("[removed] testrules_reload_1.js")

	// check rule again
	s.publish("/devices/vdev1/controls/qqq/on", "1", "vdev1/qqq")
	s.VerifyUnordered(
		"tst -> /devices/vdev1/controls/qqq/on: [1] (QoS 1)",
		"driver -> /devices/vdev1/controls/qqq: [1] (QoS 1, retained)",
		"[info] detectRun1: vdev1/qqq (s=false, a=10)",
		"[info] detectRun: vdev1/qqq (s=false, a=10)",
	)
	s.VerifyEmpty()
}

func (s *RuleReloadSuite) TestRemoveRestore() {
	s.RemoveScript("testrules_reload_2.js")
	s.VerifyVdevCleanup("testrules_reload_2.js")
	s.Verify(
		// removal notification for the client-side script editor
		"[removed] testrules_reload_2.js",
	)

	// load script and expect vdev definition at least
	s.LiveLoadScript("testrules_reload_2.js")
	// s.ReplaceScript("testrules_reload_2.js", "testrules_reload_2_changed.js")
	s.SkipTill("[changed] testrules_reload_2.js")

	s.VerifyRules()
}

func (s *RuleReloadSuite) TestNoReloading() {
	s.engine.EvalScript("testrules_reload_1_loaded = false;")
	// no actual replacement should happen here
	s.ReplaceScript("testrules_reload_1.js", "testrules_reload_1.js")
	s.Ck("unexpected reload", s.engine.EvalScript(
		"if(testrules_reload_1_loaded) throw new Error('unexpected reload')"))
	s.VerifyEmpty()
}

func (s *RuleReloadSuite) verifyReloadCount(n int) {
	script := fmt.Sprintf(
		`if(testrules_reload_2_n != %d)
                   throw new Error(
                     "bad reload count: " + testrules_reload_2_n + " instead of " + %d)`,
		n, n)
	s.Ck("bad reload count", s.engine.EvalScript(script))
}

func (s *RuleReloadSuite) TestOverwriteScript() {
	// let's load script from a new directory
	s.Ck("OverwriteScript()",
		s.OverwriteScript("subdir/testrules_reload_42.js", "testrules_reload_2_changed.js"))
	s.Verify("[info] Error: Device with given ID already exists")

	s.EnsureGotErrors() // got warning for vdev redefinition
	s.verifyReloadCount(1)
	s.SkipTill("[changed] subdir/testrules_reload_42.js")

	// the following ReplaceScript() which calls LiveLoadFile()
	// has now effect because the new content is already registered
	s.ReplaceScript("testrules_reload_2.js", "testrules_reload_2_changed.js")
	s.verifyReloadCount(2)
	s.SkipTill("[changed] testrules_reload_2.js")
}

func (s *RuleReloadSuite) TestWriteScript() {
	for n := 1; n < 3; n++ {
		// OverwriteScript() calls LiveWriteScript() which causes
		// script to reload every time
		s.Ck("OverwriteScript()",
			s.OverwriteScript("testrules_reload_2.js", "testrules_reload_2_changed.js"))
		s.verifyReloadCount(n)
		s.SkipTill("[changed] testrules_reload_2.js")
	}
}

func TestRuleReloadSuite(t *testing.T) {
	testutils.RunSuites(t,
		new(RuleReloadSuite),
	)
}
<|endoftext|>


<commit_before>package citrixadc

import (
	"github.com/chiradeep/go-nitro/config/gslb"

	"github.com/chiradeep/go-nitro/netscaler"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"

	"fmt"
	"log"
)

func resourceCitrixAdcGslbsite() *schema.Resource {
	return &schema.Resource{
		SchemaVersion: 1,
		Create:        createGslbsiteFunc,
		Read:          readGslbsiteFunc,
		Update:        updateGslbsiteFunc,
		Delete:        deleteGslbsiteFunc,
		Schema: map[string]*schema.Schema{
			"clip": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
				ForceNew: true,
			},
			"metricexchange": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"naptrreplacementsuffix": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"nwmetricexchange": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"parentsite": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
				ForceNew: true,
			},
			"publicclip": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
				ForceNew: true,
			},
			"publicip": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
				ForceNew: true,
			},
			"sessionexchange": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"siteipaddress": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
				ForceNew: true,
			},
			"sitename": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"sitetype": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
				ForceNew: true,
			},
			"triggermonitor": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
		},
	}
}

func createGslbsiteFunc(d *schema.ResourceData, meta interface{}) error {
	log.Printf("[DEBUG]  netscaler-provider: In createGslbsiteFunc")
	client := meta.(*NetScalerNitroClient).client
	var gslbsiteName string
	if v, ok := d.GetOk("sitename"); ok {
		gslbsiteName = v.(string)
	} else {
		gslbsiteName = resource.PrefixedUniqueId("tf-gslbsite-")
		d.Set("sitename", gslbsiteName)
	}
	gslbsite := gslb.Gslbsite{
		Clip:                   d.Get("clip").(string),
		Metricexchange:         d.Get("metricexchange").(string),
		Naptrreplacementsuffix: d.Get("naptrreplacementsuffix").(string),
		Nwmetricexchange:       d.Get("nwmetricexchange").(string),
		Parentsite:             d.Get("parentsite").(string),
		Publicclip:             d.Get("publicclip").(string),
		Publicip:               d.Get("publicip").(string),
		Sessionexchange:        d.Get("sessionexchange").(string),
		Siteipaddress:          d.Get("siteipaddress").(string),
		Sitename:               d.Get("sitename").(string),
		Sitetype:               d.Get("sitetype").(string),
		Triggermonitor:         d.Get("triggermonitor").(string),
	}

	_, err := client.AddResource(netscaler.Gslbsite.Type(), gslbsiteName, &gslbsite)
	if err != nil {
		return err
	}

	d.SetId(gslbsiteName)

	err = readGslbsiteFunc(d, meta)
	if err != nil {
		log.Printf("[ERROR] netscaler-provider: ?? we just created this gslbsite but we can't read it ?? %s", gslbsiteName)
		return nil
	}
	return nil
}

func readGslbsiteFunc(d *schema.ResourceData, meta interface{}) error {
	log.Printf("[DEBUG] netscaler-provider:  In readGslbsiteFunc")
	client := meta.(*NetScalerNitroClient).client
	gslbsiteName := d.Id()
	log.Printf("[DEBUG] netscaler-provider: Reading gslbsite state %s", gslbsiteName)
	data, err := client.FindResource(netscaler.Gslbsite.Type(), gslbsiteName)
	if err != nil {
		log.Printf("[WARN] netscaler-provider: Clearing gslbsite state %s", gslbsiteName)
		d.SetId("")
		return nil
	}
	d.Set("sitename", data["sitename"])
	d.Set("clip", data["clip"])
	d.Set("metricexchange", data["metricexchange"])
	d.Set("naptrreplacementsuffix", data["naptrreplacementsuffix"])
	d.Set("nwmetricexchange", data["nwmetricexchange"])
	d.Set("parentsite", data["parentsite"])
	d.Set("publicclip", data["publicclip"])
	d.Set("publicip", data["publicip"])
	d.Set("sessionexchange", data["sessionexchange"])
	d.Set("siteipaddress", data["siteipaddress"])
	d.Set("sitename", data["sitename"])
	d.Set("sitetype", data["sitetype"])
	d.Set("triggermonitor", data["triggermonitor"])

	return nil

}

func updateGslbsiteFunc(d *schema.ResourceData, meta interface{}) error {
	log.Printf("[DEBUG]  netscaler-provider: In updateGslbsiteFunc")
	client := meta.(*NetScalerNitroClient).client
	gslbsiteName := d.Get("sitename").(string)

	gslbsite := gslb.Gslbsite{
		Sitename: d.Get("sitename").(string),
	}
	hasChange := false
	if d.HasChange("clip") {
		log.Printf("[DEBUG]  netscaler-provider: Clip has changed for gslbsite %s, starting update", gslbsiteName)
		gslbsite.Clip = d.Get("clip").(string)
		hasChange = true
	}
	if d.HasChange("metricexchange") {
		log.Printf("[DEBUG]  netscaler-provider: Metricexchange has changed for gslbsite %s, starting update", gslbsiteName)
		gslbsite.Metricexchange = d.Get("metricexchange").(string)
		hasChange = true
	}
	if d.HasChange("naptrreplacementsuffix") {
		log.Printf("[DEBUG]  netscaler-provider: Naptrreplacementsuffix has changed for gslbsite %s, starting update", gslbsiteName)
		gslbsite.Naptrreplacementsuffix = d.Get("naptrreplacementsuffix").(string)
		hasChange = true
	}
	if d.HasChange("nwmetricexchange") {
		log.Printf("[DEBUG]  netscaler-provider: Nwmetricexchange has changed for gslbsite %s, starting update", gslbsiteName)
		gslbsite.Nwmetricexchange = d.Get("nwmetricexchange").(string)
		hasChange = true
	}
	if d.HasChange("parentsite") {
		log.Printf("[DEBUG]  netscaler-provider: Parentsite has changed for gslbsite %s, starting update", gslbsiteName)
		gslbsite.Parentsite = d.Get("parentsite").(string)
		hasChange = true
	}
	if d.HasChange("publicclip") {
		log.Printf("[DEBUG]  netscaler-provider: Publicclip has changed for gslbsite %s, starting update", gslbsiteName)
		gslbsite.Publicclip = d.Get("publicclip").(string)
		hasChange = true
	}
	if d.HasChange("publicip") {
		log.Printf("[DEBUG]  netscaler-provider: Publicip has changed for gslbsite %s, starting update", gslbsiteName)
		gslbsite.Publicip = d.Get("publicip").(string)
		hasChange = true
	}
	if d.HasChange("sessionexchange") {
		log.Printf("[DEBUG]  netscaler-provider: Sessionexchange has changed for gslbsite %s, starting update", gslbsiteName)
		gslbsite.Sessionexchange = d.Get("sessionexchange").(string)
		hasChange = true
	}
	if d.HasChange("siteipaddress") {
		log.Printf("[DEBUG]  netscaler-provider: Siteipaddress has changed for gslbsite %s, starting update", gslbsiteName)
		gslbsite.Siteipaddress = d.Get("siteipaddress").(string)
		hasChange = true
	}
	if d.HasChange("sitename") {
		log.Printf("[DEBUG]  netscaler-provider: Sitename has changed for gslbsite %s, starting update", gslbsiteName)
		gslbsite.Sitename = d.Get("sitename").(string)
		hasChange = true
	}
	if d.HasChange("sitetype") {
		log.Printf("[DEBUG]  netscaler-provider: Sitetype has changed for gslbsite %s, starting update", gslbsiteName)
		gslbsite.Sitetype = d.Get("sitetype").(string)
		hasChange = true
	}
	if d.HasChange("triggermonitor") {
		log.Printf("[DEBUG]  netscaler-provider: Triggermonitor has changed for gslbsite %s, starting update", gslbsiteName)
		gslbsite.Triggermonitor = d.Get("triggermonitor").(string)
		hasChange = true
	}

	if hasChange {
		_, err := client.UpdateResource(netscaler.Gslbsite.Type(), gslbsiteName, &gslbsite)
		if err != nil {
			return fmt.Errorf("Error updating gslbsite %s", gslbsiteName)
		}
	}
	return readGslbsiteFunc(d, meta)
}

func deleteGslbsiteFunc(d *schema.ResourceData, meta interface{}) error {
	log.Printf("[DEBUG]  netscaler-provider: In deleteGslbsiteFunc")
	client := meta.(*NetScalerNitroClient).client
	gslbsiteName := d.Id()
	err := client.DeleteResource(netscaler.Gslbsite.Type(), gslbsiteName)
	if err != nil {
		return err
	}

	d.SetId("")

	return nil
}
<commit_msg>Implement import operation for resource gslbsite<commit_after>package citrixadc

import (
	"github.com/chiradeep/go-nitro/config/gslb"

	"github.com/chiradeep/go-nitro/netscaler"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"

	"fmt"
	"log"
)

func resourceCitrixAdcGslbsite() *schema.Resource {
	return &schema.Resource{
		SchemaVersion: 1,
		Create:        createGslbsiteFunc,
		Read:          readGslbsiteFunc,
		Update:        updateGslbsiteFunc,
		Delete:        deleteGslbsiteFunc,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},
		Schema: map[string]*schema.Schema{
			"clip": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
				ForceNew: true,
			},
			"metricexchange": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"naptrreplacementsuffix": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"nwmetricexchange": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"parentsite": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
				ForceNew: true,
			},
			"publicclip": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
				ForceNew: true,
			},
			"publicip": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
				ForceNew: true,
			},
			"sessionexchange": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"siteipaddress": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
				ForceNew: true,
			},
			"sitename": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"sitetype": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
				ForceNew: true,
			},
			"triggermonitor": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
		},
	}
}

func createGslbsiteFunc(d *schema.ResourceData, meta interface{}) error {
	log.Printf("[DEBUG]  netscaler-provider: In createGslbsiteFunc")
	client := meta.(*NetScalerNitroClient).client
	var gslbsiteName string
	if v, ok := d.GetOk("sitename"); ok {
		gslbsiteName = v.(string)
	} else {
		gslbsiteName = resource.PrefixedUniqueId("tf-gslbsite-")
		d.Set("sitename", gslbsiteName)
	}
	gslbsite := gslb.Gslbsite{
		Clip:                   d.Get("clip").(string),
		Metricexchange:         d.Get("metricexchange").(string),
		Naptrreplacementsuffix: d.Get("naptrreplacementsuffix").(string),
		Nwmetricexchange:       d.Get("nwmetricexchange").(string),
		Parentsite:             d.Get("parentsite").(string),
		Publicclip:             d.Get("publicclip").(string),
		Publicip:               d.Get("publicip").(string),
		Sessionexchange:        d.Get("sessionexchange").(string),
		Siteipaddress:          d.Get("siteipaddress").(string),
		Sitename:               d.Get("sitename").(string),
		Sitetype:               d.Get("sitetype").(string),
		Triggermonitor:         d.Get("triggermonitor").(string),
	}

	_, err := client.AddResource(netscaler.Gslbsite.Type(), gslbsiteName, &gslbsite)
	if err != nil {
		return err
	}

	d.SetId(gslbsiteName)

	err = readGslbsiteFunc(d, meta)
	if err != nil {
		log.Printf("[ERROR] netscaler-provider: ?? we just created this gslbsite but we can't read it ?? %s", gslbsiteName)
		return nil
	}
	return nil
}

func readGslbsiteFunc(d *schema.ResourceData, meta interface{}) error {
	log.Printf("[DEBUG] netscaler-provider:  In readGslbsiteFunc")
	client := meta.(*NetScalerNitroClient).client
	gslbsiteName := d.Id()
	log.Printf("[DEBUG] netscaler-provider: Reading gslbsite state %s", gslbsiteName)
	data, err := client.FindResource(netscaler.Gslbsite.Type(), gslbsiteName)
	if err != nil {
		log.Printf("[WARN] netscaler-provider: Clearing gslbsite state %s", gslbsiteName)
		d.SetId("")
		return nil
	}
	d.Set("sitename", data["sitename"])
	d.Set("clip", data["clip"])
	d.Set("metricexchange", data["metricexchange"])
	d.Set("naptrreplacementsuffix", data["naptrreplacementsuffix"])
	d.Set("nwmetricexchange", data["nwmetricexchange"])
	d.Set("parentsite", data["parentsite"])
	d.Set("publicclip", data["publicclip"])
	d.Set("publicip", data["publicip"])
	d.Set("sessionexchange", data["sessionexchange"])
	d.Set("siteipaddress", data["siteipaddress"])
	d.Set("sitename", data["sitename"])
	d.Set("sitetype", data["sitetype"])
	d.Set("triggermonitor", data["triggermonitor"])

	return nil

}

func updateGslbsiteFunc(d *schema.ResourceData, meta interface{}) error {
	log.Printf("[DEBUG]  netscaler-provider: In updateGslbsiteFunc")
	client := meta.(*NetScalerNitroClient).client
	gslbsiteName := d.Get("sitename").(string)

	gslbsite := gslb.Gslbsite{
		Sitename: d.Get("sitename").(string),
	}
	hasChange := false
	if d.HasChange("clip") {
		log.Printf("[DEBUG]  netscaler-provider: Clip has changed for gslbsite %s, starting update", gslbsiteName)
		gslbsite.Clip = d.Get("clip").(string)
		hasChange = true
	}
	if d.HasChange("metricexchange") {
		log.Printf("[DEBUG]  netscaler-provider: Metricexchange has changed for gslbsite %s, starting update", gslbsiteName)
		gslbsite.Metricexchange = d.Get("metricexchange").(string)
		hasChange = true
	}
	if d.HasChange("naptrreplacementsuffix") {
		log.Printf("[DEBUG]  netscaler-provider: Naptrreplacementsuffix has changed for gslbsite %s, starting update", gslbsiteName)
		gslbsite.Naptrreplacementsuffix = d.Get("naptrreplacementsuffix").(string)
		hasChange = true
	}
	if d.HasChange("nwmetricexchange") {
		log.Printf("[DEBUG]  netscaler-provider: Nwmetricexchange has changed for gslbsite %s, starting update", gslbsiteName)
		gslbsite.Nwmetricexchange = d.Get("nwmetricexchange").(string)
		hasChange = true
	}
	if d.HasChange("parentsite") {
		log.Printf("[DEBUG]  netscaler-provider: Parentsite has changed for gslbsite %s, starting update", gslbsiteName)
		gslbsite.Parentsite = d.Get("parentsite").(string)
		hasChange = true
	}
	if d.HasChange("publicclip") {
		log.Printf("[DEBUG]  netscaler-provider: Publicclip has changed for gslbsite %s, starting update", gslbsiteName)
		gslbsite.Publicclip = d.Get("publicclip").(string)
		hasChange = true
	}
	if d.HasChange("publicip") {
		log.Printf("[DEBUG]  netscaler-provider: Publicip has changed for gslbsite %s, starting update", gslbsiteName)
		gslbsite.Publicip = d.Get("publicip").(string)
		hasChange = true
	}
	if d.HasChange("sessionexchange") {
		log.Printf("[DEBUG]  netscaler-provider: Sessionexchange has changed for gslbsite %s, starting update", gslbsiteName)
		gslbsite.Sessionexchange = d.Get("sessionexchange").(string)
		hasChange = true
	}
	if d.HasChange("siteipaddress") {
		log.Printf("[DEBUG]  netscaler-provider: Siteipaddress has changed for gslbsite %s, starting update", gslbsiteName)
		gslbsite.Siteipaddress = d.Get("siteipaddress").(string)
		hasChange = true
	}
	if d.HasChange("sitename") {
		log.Printf("[DEBUG]  netscaler-provider: Sitename has changed for gslbsite %s, starting update", gslbsiteName)
		gslbsite.Sitename = d.Get("sitename").(string)
		hasChange = true
	}
	if d.HasChange("sitetype") {
		log.Printf("[DEBUG]  netscaler-provider: Sitetype has changed for gslbsite %s, starting update", gslbsiteName)
		gslbsite.Sitetype = d.Get("sitetype").(string)
		hasChange = true
	}
	if d.HasChange("triggermonitor") {
		log.Printf("[DEBUG]  netscaler-provider: Triggermonitor has changed for gslbsite %s, starting update", gslbsiteName)
		gslbsite.Triggermonitor = d.Get("triggermonitor").(string)
		hasChange = true
	}

	if hasChange {
		_, err := client.UpdateResource(netscaler.Gslbsite.Type(), gslbsiteName, &gslbsite)
		if err != nil {
			return fmt.Errorf("Error updating gslbsite %s", gslbsiteName)
		}
	}
	return readGslbsiteFunc(d, meta)
}

func deleteGslbsiteFunc(d *schema.ResourceData, meta interface{}) error {
	log.Printf("[DEBUG]  netscaler-provider: In deleteGslbsiteFunc")
	client := meta.(*NetScalerNitroClient).client
	gslbsiteName := d.Id()
	err := client.DeleteResource(netscaler.Gslbsite.Type(), gslbsiteName)
	if err != nil {
		return err
	}

	d.SetId("")

	return nil
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors All rights reserved.

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 (
	"flag"
	"net/url"
	"os"
	"time"

	"k8s.io/contrib/cluster-autoscaler/config"
	"k8s.io/contrib/cluster-autoscaler/simulator"
	"k8s.io/contrib/cluster-autoscaler/utils/gce"
	kube_api "k8s.io/kubernetes/pkg/api"
	kube_record "k8s.io/kubernetes/pkg/client/record"
	kube_client "k8s.io/kubernetes/pkg/client/unversioned"

	"github.com/golang/glog"
)

var (
	migConfigFlag           config.MigConfigFlag
	kubernetes              = flag.String("kubernetes", "", "Kuberentes master location. Leave blank for default")
	cloudConfig             = flag.String("cloud-config", "", "The path to the cloud provider configuration file.  Empty string for no configuration file.")
	verifyUnschedulablePods = flag.Bool("verify-unschedulable-pods", true,
		"If enabled CA will ensure that each pod marked by Scheduler as unschedulable actually can't be scheduled on any node."+
			"This prevents from adding unnecessary nodes in situation when CA and Scheduler have different configuration.")
	scaleDownEnabled = flag.Bool("experimental-scale-down-enabled", false, "Should CA scale down the cluster")
	scaleDownDelay   = flag.Duration("scale-down-delay", 10*time.Minute,
		"Duration from the last scale up to the time when CA starts to check scale down options")
	scaleDownUnderutilizedTime = flag.Duration("scale-down-underutilized-time", 10*time.Minute,
		"How long the node should be underutilized before it is eligible for scale down")
	scaleDownUtilizationThreshold = flag.Float64("scale-down-utilization-threshold", 0.5,
		"Node reservation level below which a node can be considered for scale down")
	scaleDownTrialFrequency = flag.Duration("scale-down-trial-frequency", 10*time.Minute,
		"How often scale down possiblity is check")
)

func main() {
	flag.Var(&migConfigFlag, "nodes", "sets min,max size and url of a MIG to be controlled by Cluster Autoscaler. "+
		"Can be used multiple times. Format: <min>:<max>:<migurl>")
	flag.Parse()

	url, err := url.Parse(*kubernetes)
	if err != nil {
		glog.Fatalf("Failed to parse Kuberentes url: %v", err)
	}

	// Configuration
	kubeConfig, err := config.GetKubeClientConfig(url)
	if err != nil {
		glog.Fatalf("Failed to build Kuberentes client configuration: %v", err)
	}
	migConfigs := make([]*config.MigConfig, 0, len(migConfigFlag))
	for i := range migConfigFlag {
		migConfigs = append(migConfigs, &migConfigFlag[i])
	}

	// GCE Manager
	var config *os.File
	if *cloudConfig != "" {
		config, err = os.Open(*cloudConfig)
		if err != nil {
			glog.Fatalf("Couldn't open cloud provider configuration %s: %#v", *cloudConfig, err)
		}
		defer config.Close()
	}
	gceManager, err := gce.CreateGceManager(migConfigs, config)
	if err != nil {
		glog.Fatalf("Failed to create GCE Manager: %v", err)
	}

	kubeClient := kube_client.NewOrDie(kubeConfig)

	predicateChecker, err := simulator.NewPredicateChecker(kubeClient)
	if err != nil {
		glog.Fatalf("Failed to create predicate checker: %v", err)
	}
	unschedulablePodLister := NewUnschedulablePodLister(kubeClient)
	scheduledPodLister := NewScheduledPodLister(kubeClient)
	nodeLister := NewNodeLister(kubeClient)

	lastScaleUpTime := time.Now()
	lastScaleDownFailedTrial := time.Now()
	underutilizedNodes := make(map[string]time.Time)

	eventBroadcaster := kube_record.NewBroadcaster()
	eventBroadcaster.StartLogging(glog.Infof)
	eventBroadcaster.StartRecordingToSink(kubeClient.Events(""))
	recorder := eventBroadcaster.NewRecorder(kube_api.EventSource{Component: "cluster-autoscaler"})

	for {
		select {
		case <-time.After(time.Minute):
			{
				nodes, err := nodeLister.List()
				if err != nil {
					glog.Errorf("Failed to list nodes: %v", err)
					continue
				}
				if len(nodes) == 0 {
					glog.Errorf("No nodes in the cluster")
					continue
				}

				if err := CheckMigsAndNodes(nodes, gceManager); err != nil {
					glog.Warningf("Cluster is not ready for autoscaling: %v", err)
					continue
				}

				allUnschedulablePods, err := unschedulablePodLister.List()
				if err != nil {
					glog.Errorf("Failed to list unscheduled pods: %v", err)
					continue
				}

				allScheduled, err := scheduledPodLister.List()
				if err != nil {
					glog.Errorf("Failed to list scheduled pods: %v", err)
					continue
				}

				// We need to reset all pods that have been marked as unschedulable not after
				// the newest node became available for the scheduler.
				allNodesAvailableTime := GetAllNodesAvailableTime(nodes)
				podsToReset, unschedulablePodsToHelp := SlicePodsByPodScheduledTime(allUnschedulablePods, allNodesAvailableTime)
				ResetPodScheduledCondition(kubeClient, podsToReset)

				// We need to check whether pods marked as unschedulable are actually unschedulable.
				// This should prevent from adding unnecessary nodes. Example of such situation:
				// - CA and Scheduler has slightly different configuration
				// - Scheduler can't schedule a pod and marks it as unschedulable
				// - CA added a node which should help the pod
				// - Scheduler doesn't schedule the pod on the new node
				//   because according to it logic it doesn't fit there
				// - CA see the pod is still unschedulable, so it adds another node to help it
				//
				// With the check enabled the last point won't happen because CA will ignore a pod
				// which is supposed to schedule on an existing node.
				//
				// Without below check cluster might be unnecessary scaled up to the max allowed size
				// in the describe situation.
				schedulablePodsPresent := false
				if *verifyUnschedulablePods {
					newUnschedulablePodsToHelp := FilterOutSchedulable(unschedulablePodsToHelp, nodes, allScheduled, predicateChecker)

					if len(newUnschedulablePodsToHelp) != len(unschedulablePodsToHelp) {
						schedulablePodsPresent = true
					}
					unschedulablePodsToHelp = newUnschedulablePodsToHelp
				}

				if len(unschedulablePodsToHelp) == 0 {
					glog.V(1).Info("No unschedulable pods")
				} else {
					scaledUp, err := ScaleUp(unschedulablePodsToHelp, nodes, migConfigs, gceManager, kubeClient, predicateChecker, recorder)
					if err != nil {
						glog.Errorf("Failed to scale up: %v", err)
						continue
					} else {
						if scaledUp {
							lastScaleUpTime = time.Now()
							// No scale down in this iteration.
							continue
						}
					}
				}

				if *scaleDownEnabled {
					// In dry run only utilization is updated
					calculateUtilizationOnly := lastScaleUpTime.Add(*scaleDownDelay).After(time.Now()) ||
						lastScaleDownFailedTrial.Add(*scaleDownTrialFrequency).After(time.Now()) ||
						schedulablePodsPresent

					underutilizedNodes = CalculateUnderutilizedNodes(
						nodes,
						underutilizedNodes,
						*scaleDownUtilizationThreshold,
						allScheduled,
						predicateChecker)

					if !calculateUtilizationOnly {
						result, err := ScaleDown(
							nodes,
							underutilizedNodes,
							*scaleDownUnderutilizedTime,
							allScheduled,
							gceManager, kubeClient, predicateChecker)
						if err != nil {
							glog.Errorf("Failed to scale down: %v", err)
						} else {
							if result == ScaleDownNodeDeleted {
								// Clean the utilization map to be super sure that the simulated
								// deletions are made in the new context.
								underutilizedNodes = make(map[string]time.Time, len(underutilizedNodes))
							} else {
								lastScaleDownFailedTrial = time.Now()
							}
						}
					}
				}
			}
		}
	}
}
<commit_msg>Fixed issue with not provided cloud config<commit_after>/*
Copyright 2016 The Kubernetes Authors All rights reserved.

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 (
	"flag"
	"net/url"
	"os"
	"time"

	"k8s.io/contrib/cluster-autoscaler/config"
	"k8s.io/contrib/cluster-autoscaler/simulator"
	"k8s.io/contrib/cluster-autoscaler/utils/gce"
	kube_api "k8s.io/kubernetes/pkg/api"
	kube_record "k8s.io/kubernetes/pkg/client/record"
	kube_client "k8s.io/kubernetes/pkg/client/unversioned"

	"github.com/golang/glog"
)

var (
	migConfigFlag           config.MigConfigFlag
	kubernetes              = flag.String("kubernetes", "", "Kuberentes master location. Leave blank for default")
	cloudConfig             = flag.String("cloud-config", "", "The path to the cloud provider configuration file.  Empty string for no configuration file.")
	verifyUnschedulablePods = flag.Bool("verify-unschedulable-pods", true,
		"If enabled CA will ensure that each pod marked by Scheduler as unschedulable actually can't be scheduled on any node."+
			"This prevents from adding unnecessary nodes in situation when CA and Scheduler have different configuration.")
	scaleDownEnabled = flag.Bool("experimental-scale-down-enabled", false, "Should CA scale down the cluster")
	scaleDownDelay   = flag.Duration("scale-down-delay", 10*time.Minute,
		"Duration from the last scale up to the time when CA starts to check scale down options")
	scaleDownUnderutilizedTime = flag.Duration("scale-down-underutilized-time", 10*time.Minute,
		"How long the node should be underutilized before it is eligible for scale down")
	scaleDownUtilizationThreshold = flag.Float64("scale-down-utilization-threshold", 0.5,
		"Node reservation level below which a node can be considered for scale down")
	scaleDownTrialFrequency = flag.Duration("scale-down-trial-frequency", 10*time.Minute,
		"How often scale down possiblity is check")
)

func main() {
	flag.Var(&migConfigFlag, "nodes", "sets min,max size and url of a MIG to be controlled by Cluster Autoscaler. "+
		"Can be used multiple times. Format: <min>:<max>:<migurl>")
	flag.Parse()

	url, err := url.Parse(*kubernetes)
	if err != nil {
		glog.Fatalf("Failed to parse Kuberentes url: %v", err)
	}

	// Configuration
	kubeConfig, err := config.GetKubeClientConfig(url)
	if err != nil {
		glog.Fatalf("Failed to build Kuberentes client configuration: %v", err)
	}
	migConfigs := make([]*config.MigConfig, 0, len(migConfigFlag))
	for i := range migConfigFlag {
		migConfigs = append(migConfigs, &migConfigFlag[i])
	}

	// GCE Manager
	var gceManager *gce.GceManager
	if *cloudConfig != "" {
		config, err := os.Open(*cloudConfig)
		if err != nil {
			glog.Fatalf("Couldn't open cloud provider configuration %s: %#v", *cloudConfig, err)
		}
		defer config.Close()
		gceManager, err = gce.CreateGceManager(migConfigs, config)
	} else {
		gceManager, err = gce.CreateGceManager(migConfigs, nil)
	}
	if err != nil {
		glog.Fatalf("Failed to create GCE Manager: %v", err)
	}

	kubeClient := kube_client.NewOrDie(kubeConfig)

	predicateChecker, err := simulator.NewPredicateChecker(kubeClient)
	if err != nil {
		glog.Fatalf("Failed to create predicate checker: %v", err)
	}
	unschedulablePodLister := NewUnschedulablePodLister(kubeClient)
	scheduledPodLister := NewScheduledPodLister(kubeClient)
	nodeLister := NewNodeLister(kubeClient)

	lastScaleUpTime := time.Now()
	lastScaleDownFailedTrial := time.Now()
	underutilizedNodes := make(map[string]time.Time)

	eventBroadcaster := kube_record.NewBroadcaster()
	eventBroadcaster.StartLogging(glog.Infof)
	eventBroadcaster.StartRecordingToSink(kubeClient.Events(""))
	recorder := eventBroadcaster.NewRecorder(kube_api.EventSource{Component: "cluster-autoscaler"})

	for {
		select {
		case <-time.After(time.Minute):
			{
				nodes, err := nodeLister.List()
				if err != nil {
					glog.Errorf("Failed to list nodes: %v", err)
					continue
				}
				if len(nodes) == 0 {
					glog.Errorf("No nodes in the cluster")
					continue
				}

				if err := CheckMigsAndNodes(nodes, gceManager); err != nil {
					glog.Warningf("Cluster is not ready for autoscaling: %v", err)
					continue
				}

				allUnschedulablePods, err := unschedulablePodLister.List()
				if err != nil {
					glog.Errorf("Failed to list unscheduled pods: %v", err)
					continue
				}

				allScheduled, err := scheduledPodLister.List()
				if err != nil {
					glog.Errorf("Failed to list scheduled pods: %v", err)
					continue
				}

				// We need to reset all pods that have been marked as unschedulable not after
				// the newest node became available for the scheduler.
				allNodesAvailableTime := GetAllNodesAvailableTime(nodes)
				podsToReset, unschedulablePodsToHelp := SlicePodsByPodScheduledTime(allUnschedulablePods, allNodesAvailableTime)
				ResetPodScheduledCondition(kubeClient, podsToReset)

				// We need to check whether pods marked as unschedulable are actually unschedulable.
				// This should prevent from adding unnecessary nodes. Example of such situation:
				// - CA and Scheduler has slightly different configuration
				// - Scheduler can't schedule a pod and marks it as unschedulable
				// - CA added a node which should help the pod
				// - Scheduler doesn't schedule the pod on the new node
				//   because according to it logic it doesn't fit there
				// - CA see the pod is still unschedulable, so it adds another node to help it
				//
				// With the check enabled the last point won't happen because CA will ignore a pod
				// which is supposed to schedule on an existing node.
				//
				// Without below check cluster might be unnecessary scaled up to the max allowed size
				// in the describe situation.
				schedulablePodsPresent := false
				if *verifyUnschedulablePods {
					newUnschedulablePodsToHelp := FilterOutSchedulable(unschedulablePodsToHelp, nodes, allScheduled, predicateChecker)

					if len(newUnschedulablePodsToHelp) != len(unschedulablePodsToHelp) {
						schedulablePodsPresent = true
					}
					unschedulablePodsToHelp = newUnschedulablePodsToHelp
				}

				if len(unschedulablePodsToHelp) == 0 {
					glog.V(1).Info("No unschedulable pods")
				} else {
					scaledUp, err := ScaleUp(unschedulablePodsToHelp, nodes, migConfigs, gceManager, kubeClient, predicateChecker, recorder)
					if err != nil {
						glog.Errorf("Failed to scale up: %v", err)
						continue
					} else {
						if scaledUp {
							lastScaleUpTime = time.Now()
							// No scale down in this iteration.
							continue
						}
					}
				}

				if *scaleDownEnabled {
					// In dry run only utilization is updated
					calculateUtilizationOnly := lastScaleUpTime.Add(*scaleDownDelay).After(time.Now()) ||
						lastScaleDownFailedTrial.Add(*scaleDownTrialFrequency).After(time.Now()) ||
						schedulablePodsPresent

					underutilizedNodes = CalculateUnderutilizedNodes(
						nodes,
						underutilizedNodes,
						*scaleDownUtilizationThreshold,
						allScheduled,
						predicateChecker)

					if !calculateUtilizationOnly {
						result, err := ScaleDown(
							nodes,
							underutilizedNodes,
							*scaleDownUnderutilizedTime,
							allScheduled,
							gceManager, kubeClient, predicateChecker)
						if err != nil {
							glog.Errorf("Failed to scale down: %v", err)
						} else {
							if result == ScaleDownNodeDeleted {
								// Clean the utilization map to be super sure that the simulated
								// deletions are made in the new context.
								underutilizedNodes = make(map[string]time.Time, len(underutilizedNodes))
							} else {
								lastScaleDownFailedTrial = time.Now()
							}
						}
					}
				}
			}
		}
	}
}
<|endoftext|>


<commit_before>package main

// memorydiskserver: the archetypal (worst-case) stateful application. a web
// server, which:
// * loads an integer from a file on disk into memory, if it is present, when
//   it starts up
// * has an endpoint to set the integer, but only sets it in memory
// * writes the integer from memory to disk when asked nicely to shut down (SIGTERM)
// * has an endpoint to read the integer from memory
//
// this allows us to test the following cases:
// * that docker containers stopped and started cleanly around a commit operation
// * that switching branches results in container being stopped and started
//   with new on-disk state
// * that resetting results in container being stopped and started with new
//   on-disk state

/*

$ curl -sSL http://localhost/set?value=10
$ curl -sSL http://localhost/get
10
$ dvol commit -m "value=10"
$ curl -sSL http://localhost/set?value=20
$ curl -sSL http://localhost/get
20
$ dvol reset --hard HEAD
$ curl -sSL http://localhost/get
10

*/

//
// this application can be built and copied into a docker image "FROM scratch"
// (which involves no network operation)

import (
    "fmt"
    "net/http"
)

var theValue string

func getHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Value: %s", theValue)
}

func setHandler(w http.ResponseWriter, r *http.Request) {
    theValue = r.URL.Query()["value"][0]
}

//func setHandler(w http.ResponseWriter, r *http.Request) {
//    fmt.Fprintf(w, "Hi there, I love %s!", r.URL.Path[1:])
//}

//func signalHandler(chan c) {
//    // block until we receive a SIGTERM
//    s := <-c
//    writeValueToDisk()
//    os.Exit(0)
//}

func main() {
    http.HandleFunc("/get", getHandler)
    http.HandleFunc("/set", setHandler)

//    c := make(chan os.Signal, 1)
//    signal.Notify(c, os.Interrupt)
//    go signalHandler(c)

    http.ListenAndServe(":80", nil)
}
<commit_msg>Pretty up a comment<commit_after>package main

/* memorydiskserver: a testing utility.

the archetypal (worst-case) stateful application. a web server, which:
* loads an integer from a file on disk into memory, if it is present, when
  it starts up
* has an endpoint to set the integer, but only sets it in memory
* writes the integer from memory to disk when asked nicely to shut down (SIGTERM)
* has an endpoint to read the integer from memory

this allows us to test the following cases:
* that docker containers stopped and started cleanly around a commit operation
* that switching branches results in container being stopped and started
  with new on-disk state
* that resetting results in container being stopped and started with new
  on-disk state

this application can be built and copied into a docker image "FROM scratch"
(which involves no network operation)

*/

/*

$ curl -sSL http://localhost/set?value=10
$ curl -sSL http://localhost/get
10
$ dvol commit -m "value=10"
$ curl -sSL http://localhost/set?value=20
$ curl -sSL http://localhost/get
20
$ dvol reset --hard HEAD
$ curl -sSL http://localhost/get
10

*/

import (
    "fmt"
    "net/http"
)

var theValue string

func getHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Value: %s", theValue)
}

func setHandler(w http.ResponseWriter, r *http.Request) {
    theValue = r.URL.Query()["value"][0]
}

//func setHandler(w http.ResponseWriter, r *http.Request) {
//    fmt.Fprintf(w, "Hi there, I love %s!", r.URL.Path[1:])
//}

//func signalHandler(chan c) {
//    // block until we receive a SIGTERM
//    s := <-c
//    writeValueToDisk()
//    os.Exit(0)
//}

func main() {
    http.HandleFunc("/get", getHandler)
    http.HandleFunc("/set", setHandler)

//    c := make(chan os.Signal, 1)
//    signal.Notify(c, os.Interrupt)
//    go signalHandler(c)

    http.ListenAndServe(":80", nil)
}
<|endoftext|>