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

// Package websocket implements a client and server for the WebSocket protocol.
// The protocol is defined at http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol
package websocket

import (
	"bufio"
	"crypto/tls"
	"http"
	"io"
	"io/ioutil"
	"json"
	"net"
	"os"
	"url"
)

const (
	ProtocolVersionHixie75 = -75
	ProtocolVersionHixie76 = -76
	ProtocolVersionHybi00  = 0
	ProtocolVersionHybi    = 8

	ContinuationFrame = 0
	TextFrame         = 1
	BinaryFrame       = 2
	CloseFrame        = 8
	PingFrame         = 9
	PongFrame         = 10
	UnknownFrame      = 255
)

// WebSocket protocol errors.
type ProtocolError struct {
	ErrorString string
}

func (err ProtocolError) String() string { return err.ErrorString }

var (
	ErrBadProtocolVersion   = ProtocolError{"bad protocol version"}
	ErrBadScheme            = ProtocolError{"bad scheme"}
	ErrBadStatus            = ProtocolError{"bad status"}
	ErrBadUpgrade           = ProtocolError{"missing or bad upgrade"}
	ErrBadWebSocketOrigin   = ProtocolError{"missing or bad WebSocket-Origin"}
	ErrBadWebSocketLocation = ProtocolError{"missing or bad WebSocket-Location"}
	ErrBadWebSocketProtocol = ProtocolError{"missing or bad WebSocket-Protocol"}
	ErrBadWebSocketVersion  = ProtocolError{"missing or bad WebSocket Version"}
	ErrChallengeResponse    = ProtocolError{"mismatch challenge/response"}
	ErrBadFrame             = ProtocolError{"bad frame"}
	ErrBadFrameBoundary     = ProtocolError{"not on frame boundary"}
	ErrNotWebSocket         = ProtocolError{"not websocket protocol"}
	ErrBadRequestMethod     = ProtocolError{"bad method"}
	ErrNotSupported         = ProtocolError{"not supported"}
)

// WebSocketAddr is an implementation of net.Addr for WebSocket.
type WebSocketAddr struct {
	*url.URL
}

// Network returns the network type for a WebSocket, "websocket".
func (addr WebSocketAddr) Network() string { return "websocket" }

// Config is a WebSocket configuration
type Config struct {
	// A WebSocket server address.
	Location *url.URL

	// A Websocket client origin.
	Origin *url.URL

	// WebSocket subprotocols.
	Protocol []string

	// WebSocket protocol version.
	Version int

	// TLS config for secure WebSocket (wss).
	TlsConfig *tls.Config

	handshakeData map[string]string
}

// serverHandshaker is an interface to handle WebSocket server side handshake.
type serverHandshaker interface {
	// ReadHandshake reads handshake request message from client.
	// Returns http response code and error if any.
	ReadHandshake(buf *bufio.Reader, req *http.Request) (code int, err os.Error)

	// AcceptHandshake accepts the client handshake request and sends
	// handshake response back to client.
	AcceptHandshake(buf *bufio.Writer) (err os.Error)

	// NewServerConn creates a new WebSocket connection.
	NewServerConn(buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) (conn *Conn)
}

// frameReader is an interface to read a WebSocket frame.
type frameReader interface {
	// Reader is to read payload of the frame.
	io.Reader

	// PayloadType returns payload type.
	PayloadType() byte

	// HeaderReader returns a reader to read header of the frame.
	HeaderReader() io.Reader

	// TrailerReader returns a reader to read trailer of the frame.
	// If it returns nil, there is no trailer in the frame.
	TrailerReader() io.Reader

	// Len returns total length of the frame, including header and trailer.
	Len() int
}

// frameReaderFactory is an interface to creates new frame reader.
type frameReaderFactory interface {
	NewFrameReader() (r frameReader, err os.Error)
}

// frameWriter is an interface to write a WebSocket frame.
type frameWriter interface {
	// Writer is to write playload of the frame.
	io.WriteCloser
}

// frameWriterFactory is an interface to create new frame writer.
type frameWriterFactory interface {
	NewFrameWriter(payloadType byte) (w frameWriter, err os.Error)
}

type frameHandler interface {
	HandleFrame(frame frameReader) (r frameReader, err os.Error)
	WriteClose(status int) (err os.Error)
}

// Conn represents a WebSocket connection.
type Conn struct {
	config  *Config
	request *http.Request

	buf *bufio.ReadWriter
	rwc io.ReadWriteCloser

	frameReaderFactory
	frameReader

	frameWriterFactory

	frameHandler
	PayloadType        byte
	defaultCloseStatus int
}

// Read implements the io.Reader interface:
// it reads data of a frame from the WebSocket connection.
// if msg is not large enough for the frame data, it fills the msg and next Read
// will read the rest of the frame data.
// it reads Text frame or Binary frame.
func (ws *Conn) Read(msg []byte) (n int, err os.Error) {
again:
	if ws.frameReader == nil {
		frame, err := ws.frameReaderFactory.NewFrameReader()
		if err != nil {
			return 0, err
		}
		ws.frameReader, err = ws.frameHandler.HandleFrame(frame)
		if err != nil {
			return 0, err
		}
		if ws.frameReader == nil {
			goto again
		}
	}
	n, err = ws.frameReader.Read(msg)
	if err == os.EOF {
		if trailer := ws.frameReader.TrailerReader(); trailer != nil {
			io.Copy(ioutil.Discard, trailer)
		}
		ws.frameReader = nil
		goto again
	}
	return n, err
}

// Write implements the io.Writer interface:
// it writes data as a frame to the WebSocket connection.
func (ws *Conn) Write(msg []byte) (n int, err os.Error) {
	w, err := ws.frameWriterFactory.NewFrameWriter(ws.PayloadType)
	if err != nil {
		return 0, err
	}
	n, err = w.Write(msg)
	w.Close()
	if err != nil {
		return n, err
	}
	return n, err
}

// Close implements the io.Closer interface.
func (ws *Conn) Close() os.Error {
	err := ws.frameHandler.WriteClose(ws.defaultCloseStatus)
	if err != nil {
		return err
	}
	return ws.rwc.Close()
}

func (ws *Conn) IsClientConn() bool { return ws.request == nil }
func (ws *Conn) IsServerConn() bool { return ws.request != nil }

// LocalAddr returns the WebSocket Origin for the connection for client, or
// the WebSocket location for server.
func (ws *Conn) LocalAddr() net.Addr {
	if ws.IsClientConn() {
		return WebSocketAddr{ws.config.Origin}
	}
	return WebSocketAddr{ws.config.Location}
}

// RemoteAddr returns the WebSocket location for the connection for client, or
// the Websocket Origin for server.
func (ws *Conn) RemoteAddr() net.Addr {
	if ws.IsClientConn() {
		return WebSocketAddr{ws.config.Location}
	}
	return WebSocketAddr{ws.config.Origin}
}

// SetTimeout sets the connection's network timeout in nanoseconds.
func (ws *Conn) SetTimeout(nsec int64) os.Error {
	if conn, ok := ws.rwc.(net.Conn); ok {
		return conn.SetTimeout(nsec)
	}
	return os.EINVAL
}

// SetReadTimeout sets the connection's network read timeout in nanoseconds.
func (ws *Conn) SetReadTimeout(nsec int64) os.Error {
	if conn, ok := ws.rwc.(net.Conn); ok {
		return conn.SetReadTimeout(nsec)
	}
	return os.EINVAL
}

// SetWriteTimeout sets the connection's network write timeout in nanoseconds.
func (ws *Conn) SetWriteTimeout(nsec int64) os.Error {
	if conn, ok := ws.rwc.(net.Conn); ok {
		return conn.SetWriteTimeout(nsec)
	}
	return os.EINVAL
}

// Config returns the WebSocket config.
func (ws *Conn) Config() *Config { return ws.config }

// Request returns the http request upgraded to the WebSocket.
// It is nil for client side.
func (ws *Conn) Request() *http.Request { return ws.request }

// Codec represents a symmetric pair of functions that implement a codec.
type Codec struct {
	Marshal   func(v interface{}) (data []byte, payloadType byte, err os.Error)
	Unmarshal func(data []byte, payloadType byte, v interface{}) (err os.Error)
}

// Send sends v marshaled by cd.Marshal as single frame to ws.
func (cd Codec) Send(ws *Conn, v interface{}) (err os.Error) {
	if err != nil {
		return err
	}
	data, payloadType, err := cd.Marshal(v)
	if err != nil {
		return err
	}
	w, err := ws.frameWriterFactory.NewFrameWriter(payloadType)
	_, err = w.Write(data)
	w.Close()
	return err
}

// Receive receives single frame from ws, unmarshaled by cd.Unmarshal and stores in v.
func (cd Codec) Receive(ws *Conn, v interface{}) (err os.Error) {
	if ws.frameReader != nil {
		_, err = io.Copy(ioutil.Discard, ws.frameReader)
		if err != nil {
			return err
		}
		ws.frameReader = nil
	}
again:
	frame, err := ws.frameReaderFactory.NewFrameReader()
	if err != nil {
		return err
	}
	frame, err = ws.frameHandler.HandleFrame(frame)
	if err != nil {
		return err
	}
	if frame == nil {
		goto again
	}
	payloadType := frame.PayloadType()
	data, err := ioutil.ReadAll(frame)
	if err != nil {
		return err
	}
	return cd.Unmarshal(data, payloadType, v)
}

func marshal(v interface{}) (msg []byte, payloadType byte, err os.Error) {
	switch data := v.(type) {
	case string:
		return []byte(data), TextFrame, nil
	case []byte:
		return data, BinaryFrame, nil
	}
	return nil, UnknownFrame, ErrNotSupported
}

func unmarshal(msg []byte, payloadType byte, v interface{}) (err os.Error) {
	switch data := v.(type) {
	case *string:
		*data = string(msg)
		return nil
	case *[]byte:
		*data = msg
		return nil
	}
	return ErrNotSupported
}

/*
Message is a codec to send/receive text/binary data in a frame on WebSocket connection.
To send/receive text frame, use string type.
To send/receive binary frame, use []byte type.

Trivial usage:

	import "websocket"

	// receive text frame
	var message string
	websocket.Message.Receive(ws, &message)

	// send text frame
	message = "hello"
	websocket.Message.Send(ws, message)

	// receive binary frame
	var data []byte
	websocket.Message.Receive(ws, &data)

	// send binary frame
	data = []byte{0, 1, 2}
	websocket.Message.Send(ws, data)

*/
var Message = Codec{marshal, unmarshal}

func jsonMarshal(v interface{}) (msg []byte, payloadType byte, err os.Error) {
	msg, err = json.Marshal(v)
	return msg, TextFrame, err
}

func jsonUnmarshal(msg []byte, payloadType byte, v interface{}) (err os.Error) {
	return json.Unmarshal(msg, v)
}

/*
JSON is a codec to send/receive JSON data in a frame from a WebSocket connection.

Trival usage:

	import "websocket"

	type T struct {
		Msg string
		Count int
	}

	// receive JSON type T
	var data T
	websocket.JSON.Receive(ws, &data)

	// send JSON type T
	websocket.JSON.Send(ws, data)
*/
var JSON = Codec{jsonMarshal, jsonUnmarshal}
<commit_msg>websocket: rename websocket.WebSocketAddr to *websocket.Addr.<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package websocket implements a client and server for the WebSocket protocol.
// The protocol is defined at http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol
package websocket

import (
	"bufio"
	"crypto/tls"
	"http"
	"io"
	"io/ioutil"
	"json"
	"net"
	"os"
	"url"
)

const (
	ProtocolVersionHixie75 = -75
	ProtocolVersionHixie76 = -76
	ProtocolVersionHybi00  = 0
	ProtocolVersionHybi    = 8

	ContinuationFrame = 0
	TextFrame         = 1
	BinaryFrame       = 2
	CloseFrame        = 8
	PingFrame         = 9
	PongFrame         = 10
	UnknownFrame      = 255
)

// WebSocket protocol errors.
type ProtocolError struct {
	ErrorString string
}

func (err ProtocolError) String() string { return err.ErrorString }

var (
	ErrBadProtocolVersion   = ProtocolError{"bad protocol version"}
	ErrBadScheme            = ProtocolError{"bad scheme"}
	ErrBadStatus            = ProtocolError{"bad status"}
	ErrBadUpgrade           = ProtocolError{"missing or bad upgrade"}
	ErrBadWebSocketOrigin   = ProtocolError{"missing or bad WebSocket-Origin"}
	ErrBadWebSocketLocation = ProtocolError{"missing or bad WebSocket-Location"}
	ErrBadWebSocketProtocol = ProtocolError{"missing or bad WebSocket-Protocol"}
	ErrBadWebSocketVersion  = ProtocolError{"missing or bad WebSocket Version"}
	ErrChallengeResponse    = ProtocolError{"mismatch challenge/response"}
	ErrBadFrame             = ProtocolError{"bad frame"}
	ErrBadFrameBoundary     = ProtocolError{"not on frame boundary"}
	ErrNotWebSocket         = ProtocolError{"not websocket protocol"}
	ErrBadRequestMethod     = ProtocolError{"bad method"}
	ErrNotSupported         = ProtocolError{"not supported"}
)

// Addr is an implementation of net.Addr for WebSocket.
type Addr struct {
	*url.URL
}

// Network returns the network type for a WebSocket, "websocket".
func (addr *Addr) Network() string { return "websocket" }

// Config is a WebSocket configuration
type Config struct {
	// A WebSocket server address.
	Location *url.URL

	// A Websocket client origin.
	Origin *url.URL

	// WebSocket subprotocols.
	Protocol []string

	// WebSocket protocol version.
	Version int

	// TLS config for secure WebSocket (wss).
	TlsConfig *tls.Config

	handshakeData map[string]string
}

// serverHandshaker is an interface to handle WebSocket server side handshake.
type serverHandshaker interface {
	// ReadHandshake reads handshake request message from client.
	// Returns http response code and error if any.
	ReadHandshake(buf *bufio.Reader, req *http.Request) (code int, err os.Error)

	// AcceptHandshake accepts the client handshake request and sends
	// handshake response back to client.
	AcceptHandshake(buf *bufio.Writer) (err os.Error)

	// NewServerConn creates a new WebSocket connection.
	NewServerConn(buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) (conn *Conn)
}

// frameReader is an interface to read a WebSocket frame.
type frameReader interface {
	// Reader is to read payload of the frame.
	io.Reader

	// PayloadType returns payload type.
	PayloadType() byte

	// HeaderReader returns a reader to read header of the frame.
	HeaderReader() io.Reader

	// TrailerReader returns a reader to read trailer of the frame.
	// If it returns nil, there is no trailer in the frame.
	TrailerReader() io.Reader

	// Len returns total length of the frame, including header and trailer.
	Len() int
}

// frameReaderFactory is an interface to creates new frame reader.
type frameReaderFactory interface {
	NewFrameReader() (r frameReader, err os.Error)
}

// frameWriter is an interface to write a WebSocket frame.
type frameWriter interface {
	// Writer is to write playload of the frame.
	io.WriteCloser
}

// frameWriterFactory is an interface to create new frame writer.
type frameWriterFactory interface {
	NewFrameWriter(payloadType byte) (w frameWriter, err os.Error)
}

type frameHandler interface {
	HandleFrame(frame frameReader) (r frameReader, err os.Error)
	WriteClose(status int) (err os.Error)
}

// Conn represents a WebSocket connection.
type Conn struct {
	config  *Config
	request *http.Request

	buf *bufio.ReadWriter
	rwc io.ReadWriteCloser

	frameReaderFactory
	frameReader

	frameWriterFactory

	frameHandler
	PayloadType        byte
	defaultCloseStatus int
}

// Read implements the io.Reader interface:
// it reads data of a frame from the WebSocket connection.
// if msg is not large enough for the frame data, it fills the msg and next Read
// will read the rest of the frame data.
// it reads Text frame or Binary frame.
func (ws *Conn) Read(msg []byte) (n int, err os.Error) {
again:
	if ws.frameReader == nil {
		frame, err := ws.frameReaderFactory.NewFrameReader()
		if err != nil {
			return 0, err
		}
		ws.frameReader, err = ws.frameHandler.HandleFrame(frame)
		if err != nil {
			return 0, err
		}
		if ws.frameReader == nil {
			goto again
		}
	}
	n, err = ws.frameReader.Read(msg)
	if err == os.EOF {
		if trailer := ws.frameReader.TrailerReader(); trailer != nil {
			io.Copy(ioutil.Discard, trailer)
		}
		ws.frameReader = nil
		goto again
	}
	return n, err
}

// Write implements the io.Writer interface:
// it writes data as a frame to the WebSocket connection.
func (ws *Conn) Write(msg []byte) (n int, err os.Error) {
	w, err := ws.frameWriterFactory.NewFrameWriter(ws.PayloadType)
	if err != nil {
		return 0, err
	}
	n, err = w.Write(msg)
	w.Close()
	if err != nil {
		return n, err
	}
	return n, err
}

// Close implements the io.Closer interface.
func (ws *Conn) Close() os.Error {
	err := ws.frameHandler.WriteClose(ws.defaultCloseStatus)
	if err != nil {
		return err
	}
	return ws.rwc.Close()
}

func (ws *Conn) IsClientConn() bool { return ws.request == nil }
func (ws *Conn) IsServerConn() bool { return ws.request != nil }

// LocalAddr returns the WebSocket Origin for the connection for client, or
// the WebSocket location for server.
func (ws *Conn) LocalAddr() net.Addr {
	if ws.IsClientConn() {
		return &Addr{ws.config.Origin}
	}
	return &Addr{ws.config.Location}
}

// RemoteAddr returns the WebSocket location for the connection for client, or
// the Websocket Origin for server.
func (ws *Conn) RemoteAddr() net.Addr {
	if ws.IsClientConn() {
		return &Addr{ws.config.Location}
	}
	return &Addr{ws.config.Origin}
}

// SetTimeout sets the connection's network timeout in nanoseconds.
func (ws *Conn) SetTimeout(nsec int64) os.Error {
	if conn, ok := ws.rwc.(net.Conn); ok {
		return conn.SetTimeout(nsec)
	}
	return os.EINVAL
}

// SetReadTimeout sets the connection's network read timeout in nanoseconds.
func (ws *Conn) SetReadTimeout(nsec int64) os.Error {
	if conn, ok := ws.rwc.(net.Conn); ok {
		return conn.SetReadTimeout(nsec)
	}
	return os.EINVAL
}

// SetWriteTimeout sets the connection's network write timeout in nanoseconds.
func (ws *Conn) SetWriteTimeout(nsec int64) os.Error {
	if conn, ok := ws.rwc.(net.Conn); ok {
		return conn.SetWriteTimeout(nsec)
	}
	return os.EINVAL
}

// Config returns the WebSocket config.
func (ws *Conn) Config() *Config { return ws.config }

// Request returns the http request upgraded to the WebSocket.
// It is nil for client side.
func (ws *Conn) Request() *http.Request { return ws.request }

// Codec represents a symmetric pair of functions that implement a codec.
type Codec struct {
	Marshal   func(v interface{}) (data []byte, payloadType byte, err os.Error)
	Unmarshal func(data []byte, payloadType byte, v interface{}) (err os.Error)
}

// Send sends v marshaled by cd.Marshal as single frame to ws.
func (cd Codec) Send(ws *Conn, v interface{}) (err os.Error) {
	if err != nil {
		return err
	}
	data, payloadType, err := cd.Marshal(v)
	if err != nil {
		return err
	}
	w, err := ws.frameWriterFactory.NewFrameWriter(payloadType)
	_, err = w.Write(data)
	w.Close()
	return err
}

// Receive receives single frame from ws, unmarshaled by cd.Unmarshal and stores in v.
func (cd Codec) Receive(ws *Conn, v interface{}) (err os.Error) {
	if ws.frameReader != nil {
		_, err = io.Copy(ioutil.Discard, ws.frameReader)
		if err != nil {
			return err
		}
		ws.frameReader = nil
	}
again:
	frame, err := ws.frameReaderFactory.NewFrameReader()
	if err != nil {
		return err
	}
	frame, err = ws.frameHandler.HandleFrame(frame)
	if err != nil {
		return err
	}
	if frame == nil {
		goto again
	}
	payloadType := frame.PayloadType()
	data, err := ioutil.ReadAll(frame)
	if err != nil {
		return err
	}
	return cd.Unmarshal(data, payloadType, v)
}

func marshal(v interface{}) (msg []byte, payloadType byte, err os.Error) {
	switch data := v.(type) {
	case string:
		return []byte(data), TextFrame, nil
	case []byte:
		return data, BinaryFrame, nil
	}
	return nil, UnknownFrame, ErrNotSupported
}

func unmarshal(msg []byte, payloadType byte, v interface{}) (err os.Error) {
	switch data := v.(type) {
	case *string:
		*data = string(msg)
		return nil
	case *[]byte:
		*data = msg
		return nil
	}
	return ErrNotSupported
}

/*
Message is a codec to send/receive text/binary data in a frame on WebSocket connection.
To send/receive text frame, use string type.
To send/receive binary frame, use []byte type.

Trivial usage:

	import "websocket"

	// receive text frame
	var message string
	websocket.Message.Receive(ws, &message)

	// send text frame
	message = "hello"
	websocket.Message.Send(ws, message)

	// receive binary frame
	var data []byte
	websocket.Message.Receive(ws, &data)

	// send binary frame
	data = []byte{0, 1, 2}
	websocket.Message.Send(ws, data)

*/
var Message = Codec{marshal, unmarshal}

func jsonMarshal(v interface{}) (msg []byte, payloadType byte, err os.Error) {
	msg, err = json.Marshal(v)
	return msg, TextFrame, err
}

func jsonUnmarshal(msg []byte, payloadType byte, v interface{}) (err os.Error) {
	return json.Unmarshal(msg, v)
}

/*
JSON is a codec to send/receive JSON data in a frame from a WebSocket connection.

Trival usage:

	import "websocket"

	type T struct {
		Msg string
		Count int
	}

	// receive JSON type T
	var data T
	websocket.JSON.Receive(ws, &data)

	// send JSON type T
	websocket.JSON.Send(ws, data)
*/
var JSON = Codec{jsonMarshal, jsonUnmarshal}
<|endoftext|>


<commit_before>package sentry

import (
	"bufio"
	"net/http"
	"os"
	"testing"

	"github.com/mozilla-services/go-bouncer/bouncer"
	"github.com/stretchr/testify/assert"
)

type roundTripRecorder struct {
	FileName    string
	LastRequest *http.Request
}

func fileRoundTripper(f string) *roundTripRecorder {
	return &roundTripRecorder{FileName: f}
}

func (r *roundTripRecorder) RoundTrip(req *http.Request) (*http.Response, error) {
	file, err := os.Open(r.FileName)
	if err != nil {
		return nil, err
	}
	defer file.Close()
	r.LastRequest = req
	return http.ReadResponse(bufio.NewReader(file), req)
}

func TestHeadMirror(t *testing.T) {
	s := &Sentry{}

	mirror := &bouncer.MirrorsActiveResult{}

	s.roundTripper = fileRoundTripper("./fixtures/200.txt")
	err := s.HeadMirror(mirror)
	assert.NoError(t, err)

	s.roundTripper = fileRoundTripper("./fixtures/404.txt")
	err = s.HeadMirror(mirror)
	assert.NoError(t, err)

	s.roundTripper = fileRoundTripper("./fixtures/500.txt")
	err = s.HeadMirror(mirror)
	assert.EqualError(t, err, "Bad Response: 500 Internal Service Error")
}

func TestCheckLocation(t *testing.T) {
	s := &Sentry{client: DefaultClient}

	mirror := &bouncer.MirrorsActiveResult{
		BaseURL: "http://download-installer.cdn.mozilla.net/pub",
	}

	locations := []struct {
		Path string
		Lang string
	}{
		{"/firefox/releases/39.0/win32/:lang/Firefox%20Setup%2039.0.exe", "zh-TW"},
		{"/thunderbird/releases/3.1a1/win32/:lang/Firefox%20Setup%2039.0.exe", "tr"},
		{"/thunderbird/releases/39.0/win32/:lang/Firefox%20Setup%2039.0.exe", "zh-TW"},
		{"/seamonkey/releases/39.0/win32/:lang/Firefox%20Setup%2039.0.exe", "tr"},
		{"/firefox/releases/39.0/win32/:lang/Firefox%20Setup%2039.0-euBallot.exe", "sv-SE"},
	}

	for _, loc := range locations {
		location := &bouncer.LocationsActiveResult{
			Path: loc.Path,
		}

		runLog := newLockedWriter()

		rt := fileRoundTripper("./fixtures/200.binary.txt")
		s.client.Transport = rt
		res := s.checkLocation(mirror, location, runLog)
		assert.True(t, res.Active)
		assert.True(t, res.Healthy)
		assert.Contains(t, rt.LastRequest.URL.String(), "/"+loc.Lang+"/")

		rt = fileRoundTripper("./fixtures/404.txt")
		s.client.Transport = rt
		res = s.checkLocation(mirror, location, runLog)
		assert.False(t, res.Active)
		assert.False(t, res.Healthy)

		rt = fileRoundTripper("./fixtures/200.txt")
		s.client.Transport = rt
		res = s.checkLocation(mirror, location, runLog)
		assert.True(t, res.Active)
		assert.False(t, res.Healthy)

		rt = fileRoundTripper("./fixtures/500.txt")
		s.client.Transport = rt
		res = s.checkLocation(mirror, location, runLog)
		assert.True(t, res.Active)
		assert.False(t, res.Healthy)

		// Infinite redirect
		rt = fileRoundTripper("./fixtures/302.txt")
		s.client.Transport = rt
		res = s.checkLocation(mirror, location, runLog)
		assert.True(t, res.Active)
		assert.False(t, res.Healthy)
	}
}
<commit_msg>fix sentry tests<commit_after>package sentry

import (
	"bufio"
	"net/http"
	"os"
	"testing"

	"github.com/Sirupsen/logrus"
	"github.com/mozilla-services/go-bouncer/bouncer"
	"github.com/stretchr/testify/assert"
)

type roundTripRecorder struct {
	FileName    string
	LastRequest *http.Request
}

func fileRoundTripper(f string) *roundTripRecorder {
	return &roundTripRecorder{FileName: f}
}

func (r *roundTripRecorder) RoundTrip(req *http.Request) (*http.Response, error) {
	file, err := os.Open(r.FileName)
	if err != nil {
		return nil, err
	}
	defer file.Close()
	r.LastRequest = req
	return http.ReadResponse(bufio.NewReader(file), req)
}

func TestHeadMirror(t *testing.T) {
	s := &Sentry{}

	mirror := &bouncer.MirrorsActiveResult{}

	s.roundTripper = fileRoundTripper("./fixtures/200.txt")
	err := s.HeadMirror(mirror)
	assert.NoError(t, err)

	s.roundTripper = fileRoundTripper("./fixtures/404.txt")
	err = s.HeadMirror(mirror)
	assert.NoError(t, err)

	s.roundTripper = fileRoundTripper("./fixtures/500.txt")
	err = s.HeadMirror(mirror)
	assert.EqualError(t, err, "Bad Response: 500 Internal Service Error")
}

func TestCheckLocation(t *testing.T) {
	s := &Sentry{client: DefaultClient}

	mirror := &bouncer.MirrorsActiveResult{
		BaseURL: "http://download-installer.cdn.mozilla.net/pub",
	}

	locations := []struct {
		Path string
		Lang string
	}{
		{"/firefox/releases/39.0/win32/:lang/Firefox%20Setup%2039.0.exe", "zh-TW"},
		{"/thunderbird/releases/3.1a1/win32/:lang/Firefox%20Setup%2039.0.exe", "tr"},
		{"/thunderbird/releases/39.0/win32/:lang/Firefox%20Setup%2039.0.exe", "zh-TW"},
		{"/seamonkey/releases/39.0/win32/:lang/Firefox%20Setup%2039.0.exe", "tr"},
		{"/firefox/releases/39.0/win32/:lang/Firefox%20Setup%2039.0-euBallot.exe", "sv-SE"},
	}

	for _, loc := range locations {
		location := &bouncer.LocationsActiveResult{
			Path: loc.Path,
		}

		runLog := logrus.WithField("testing", true)

		rt := fileRoundTripper("./fixtures/200.binary.txt")
		s.client.Transport = rt
		res := s.checkLocation(mirror, location, runLog)
		assert.True(t, res.Active)
		assert.True(t, res.Healthy)
		assert.Contains(t, rt.LastRequest.URL.String(), "/"+loc.Lang+"/")

		rt = fileRoundTripper("./fixtures/404.txt")
		s.client.Transport = rt
		res = s.checkLocation(mirror, location, runLog)
		assert.False(t, res.Active)
		assert.False(t, res.Healthy)

		rt = fileRoundTripper("./fixtures/200.txt")
		s.client.Transport = rt
		res = s.checkLocation(mirror, location, runLog)
		assert.True(t, res.Active)
		assert.False(t, res.Healthy)

		rt = fileRoundTripper("./fixtures/500.txt")
		s.client.Transport = rt
		res = s.checkLocation(mirror, location, runLog)
		assert.True(t, res.Active)
		assert.False(t, res.Healthy)

		// Infinite redirect
		rt = fileRoundTripper("./fixtures/302.txt")
		s.client.Transport = rt
		res = s.checkLocation(mirror, location, runLog)
		assert.True(t, res.Active)
		assert.False(t, res.Healthy)
	}
}
<|endoftext|>


<commit_before>package models

import (
	"time"

	"labix.org/v2/mgo/bson"
)

type User struct {
	ObjectId      bson.ObjectId `bson:"_id" json:"-"`
	Uid           int           `bson:"uid" json:"uid"`
	Email         string        `bson:"email" json:"email"`
	LastLoginDate time.Time     `bson:"lastLoginDate" json:"lastLoginDate"`
	RegisteredAt  time.Time     `bson:"registeredAt" json:"registeredAt"`

	// TODO left this for consistency, but should be converted into Username
	Name string `bson:"username" json:"username"`

	OldName  string `bson:"oldUsername" json:"oldUserName"`
	Password string `bson:"password" json:"password"`
	Status   string `bson:"status" json:"status"`
	Salt     string `bson:"salt" json:"salt"`
	Shell    string `bson:"shell" json:"shell"`
	SshKeys  []struct {
		Title string `bson:"title"`
		Key   string `bson:"key"`
	} `bson:"sshKeys"`
}
<commit_msg>Notification: User collection EmailFrequency field is mapped to go model<commit_after>package models

import (
	"time"

	"labix.org/v2/mgo/bson"
)

type User struct {
	ObjectId      bson.ObjectId `bson:"_id" json:"-"`
	Uid           int           `bson:"uid" json:"uid"`
	Email         string        `bson:"email" json:"email"`
	LastLoginDate time.Time     `bson:"lastLoginDate" json:"lastLoginDate"`
	RegisteredAt  time.Time     `bson:"registeredAt" json:"registeredAt"`

	// TODO left this for consistency, but should be converted into Username
	Name string `bson:"username" json:"username"`

	OldName  string `bson:"oldUsername" json:"oldUserName"`
	Password string `bson:"password" json:"password"`
	Status   string `bson:"status" json:"status"`
	Salt     string `bson:"salt" json:"salt"`
	Shell    string `bson:"shell" json:"shell"`
	SshKeys  []struct {
		Title string `bson:"title"`
		Key   string `bson:"key"`
	} `bson:"sshKeys"`

	EmailFrequency map[string]bool `bson:"emailFrequency" json:"emailFrequency"`
}
<|endoftext|>


<commit_before>package validation

import (
	"testing"

	"github.com/bytom/consensus"
	"github.com/bytom/mining/tensority"
	"github.com/bytom/protocol/bc"
	"github.com/bytom/protocol/bc/types"
	"github.com/bytom/protocol/state"
	"github.com/bytom/testutil"
)

func TestCheckBlockTime(t *testing.T) {
	cases := []struct {
		blockTime  uint64
		parentTime uint64
		err        error
	}{
		{
			blockTime:  1520000001,
			parentTime: 1520000000,
			err:        nil,
		},
		{
			blockTime:  1510000000,
			parentTime: 1520000000,
			err:        errBadTimestamp,
		},
		{
			blockTime:  9999999999,
			parentTime: 1520000000,
			err:        errBadTimestamp,
		},
	}

	parent := &state.BlockNode{}
	block := &bc.Block{
		BlockHeader: &bc.BlockHeader{},
	}

	for i, c := range cases {
		parent.Timestamp = c.parentTime
		block.Timestamp = c.blockTime
		if err := checkBlockTime(block, parent); rootErr(err) != c.err {
			t.Errorf("case %d got error %s, want %s", i, err, c.err)
		}
	}
}

func TestCheckCoinbaseAmount(t *testing.T) {
	cases := []struct {
		txs    []*types.Tx
		amount uint64
		err    error
	}{
		{
			txs: []*types.Tx{
				types.NewTx(types.TxData{
					Inputs:  []*types.TxInput{types.NewCoinbaseInput(nil)},
					Outputs: []*types.TxOutput{types.NewTxOutput(*consensus.BTMAssetID, 5000, nil)},
				}),
			},
			amount: 5000,
			err:    nil,
		},
		{
			txs: []*types.Tx{
				types.NewTx(types.TxData{
					Inputs:  []*types.TxInput{types.NewCoinbaseInput(nil)},
					Outputs: []*types.TxOutput{types.NewTxOutput(*consensus.BTMAssetID, 5000, nil)},
				}),
			},
			amount: 6000,
			err:    ErrWrongCoinbaseTransaction,
		},
		{
			txs:    []*types.Tx{},
			amount: 5000,
			err:    ErrWrongCoinbaseTransaction,
		},
	}

	block := new(types.Block)
	for i, c := range cases {
		block.Transactions = c.txs
		if err := checkCoinbaseAmount(types.MapBlock(block), c.amount); rootErr(err) != c.err {
			t.Errorf("case %d got error %s, want %s", i, err, c.err)
		}
	}
}

func TestValidateBlockHeader(t *testing.T) {
	// add (hash, seed) --> (tensority hash) to the  tensority cache for avoid
	// real matrix calculate cost.
	tensority.AIHash.AddCache(&bc.Hash{V0: 0}, &bc.Hash{}, testutil.MaxHash)
	tensority.AIHash.AddCache(&bc.Hash{V0: 1}, &bc.Hash{}, testutil.MinHash)
	tensority.AIHash.AddCache(&bc.Hash{V0: 1}, consensus.InitialSeed, testutil.MinHash)

	cases := []struct {
		block  *bc.Block
		parent *state.BlockNode
		err    error
	}{
		{
			block: &bc.Block{BlockHeader: &bc.BlockHeader{
				Version: 1,
			}},
			parent: &state.BlockNode{
				Version: 2,
			},
			err: errVersionRegression,
		},
		{
			block: &bc.Block{BlockHeader: &bc.BlockHeader{
				Height: 20,
			}},
			parent: &state.BlockNode{
				Height: 18,
			},
			err: errMisorderedBlockHeight,
		},
		{
			block: &bc.Block{BlockHeader: &bc.BlockHeader{
				Height: 20,
				Bits:   0,
			}},
			parent: &state.BlockNode{
				Height: 19,
				Bits:   2305843009214532812,
			},
			err: errBadBits,
		},
		{
			block: &bc.Block{BlockHeader: &bc.BlockHeader{
				Height:          20,
				PreviousBlockId: &bc.Hash{V0: 18},
			}},
			parent: &state.BlockNode{
				Height: 19,
				Hash:   bc.Hash{V0: 19},
			},
			err: errMismatchedBlock,
		},
		{
			block: &bc.Block{
				ID: bc.Hash{V0: 0},
				BlockHeader: &bc.BlockHeader{
					Height:          1,
					Timestamp:       1523352601,
					PreviousBlockId: &bc.Hash{V0: 0},
					Bits:            2305843009214532812,
				},
			},
			parent: &state.BlockNode{
				Height:    0,
				Timestamp: 1523352600,
				Hash:      bc.Hash{V0: 0},
				Seed:      &bc.Hash{V1: 1},
				Bits:      2305843009214532812,
			},
			err: errWorkProof,
		},
		{
			block: &bc.Block{
				ID: bc.Hash{V0: 1},
				BlockHeader: &bc.BlockHeader{
					Height:          1,
					Timestamp:       1523352601,
					PreviousBlockId: &bc.Hash{V0: 0},
					Bits:            2305843009214532812,
				},
			},
			parent: &state.BlockNode{
				Height:    0,
				Timestamp: 1523352600,
				Hash:      bc.Hash{V0: 0},
				Seed:      &bc.Hash{V1: 1},
				Bits:      2305843009214532812,
			},
			err: nil,
		},
	}

	for i, c := range cases {
		if err := ValidateBlockHeader(c.block, c.parent); rootErr(err) != c.err {
			t.Errorf("case %d got error %s, want %s", i, err, c.err)
		}
	}
}
<commit_msg>validate block unit test (#1576)<commit_after>package validation

import (
	"testing"

	"github.com/bytom/consensus"
	"github.com/bytom/mining/tensority"
	"github.com/bytom/protocol/bc"
	"github.com/bytom/protocol/bc/types"
	"github.com/bytom/protocol/state"
	"github.com/bytom/testutil"
	"github.com/bytom/protocol/vm/vmutil"
)

func TestCheckBlockTime(t *testing.T) {
	cases := []struct {
		blockTime  uint64
		parentTime uint64
		err        error
	}{
		{
			blockTime:  1520000001,
			parentTime: 1520000000,
			err:        nil,
		},
		{
			blockTime:  1510000000,
			parentTime: 1520000000,
			err:        errBadTimestamp,
		},
		{
			blockTime:  9999999999,
			parentTime: 1520000000,
			err:        errBadTimestamp,
		},
	}

	parent := &state.BlockNode{}
	block := &bc.Block{
		BlockHeader: &bc.BlockHeader{},
	}

	for i, c := range cases {
		parent.Timestamp = c.parentTime
		block.Timestamp = c.blockTime
		if err := checkBlockTime(block, parent); rootErr(err) != c.err {
			t.Errorf("case %d got error %s, want %s", i, err, c.err)
		}
	}
}

func TestCheckCoinbaseAmount(t *testing.T) {
	cases := []struct {
		txs    []*types.Tx
		amount uint64
		err    error
	}{
		{
			txs: []*types.Tx{
				types.NewTx(types.TxData{
					Inputs:  []*types.TxInput{types.NewCoinbaseInput(nil)},
					Outputs: []*types.TxOutput{types.NewTxOutput(*consensus.BTMAssetID, 5000, nil)},
				}),
			},
			amount: 5000,
			err:    nil,
		},
		{
			txs: []*types.Tx{
				types.NewTx(types.TxData{
					Inputs:  []*types.TxInput{types.NewCoinbaseInput(nil)},
					Outputs: []*types.TxOutput{types.NewTxOutput(*consensus.BTMAssetID, 5000, nil)},
				}),
			},
			amount: 6000,
			err:    ErrWrongCoinbaseTransaction,
		},
		{
			txs:    []*types.Tx{},
			amount: 5000,
			err:    ErrWrongCoinbaseTransaction,
		},
	}

	block := new(types.Block)
	for i, c := range cases {
		block.Transactions = c.txs
		if err := checkCoinbaseAmount(types.MapBlock(block), c.amount); rootErr(err) != c.err {
			t.Errorf("case %d got error %s, want %s", i, err, c.err)
		}
	}
}

func TestValidateBlockHeader(t *testing.T) {
	// add (hash, seed) --> (tensority hash) to the  tensority cache for avoid
	// real matrix calculate cost.
	tensority.AIHash.AddCache(&bc.Hash{V0: 0}, &bc.Hash{}, testutil.MaxHash)
	tensority.AIHash.AddCache(&bc.Hash{V0: 1}, &bc.Hash{}, testutil.MinHash)
	tensority.AIHash.AddCache(&bc.Hash{V0: 1}, consensus.InitialSeed, testutil.MinHash)

	cases := []struct {
		block  *bc.Block
		parent *state.BlockNode
		err    error
	}{
		{
			block: &bc.Block{BlockHeader: &bc.BlockHeader{
				Version: 1,
			}},
			parent: &state.BlockNode{
				Version: 2,
			},
			err: errVersionRegression,
		},
		{
			block: &bc.Block{BlockHeader: &bc.BlockHeader{
				Height: 20,
			}},
			parent: &state.BlockNode{
				Height: 18,
			},
			err: errMisorderedBlockHeight,
		},
		{
			block: &bc.Block{BlockHeader: &bc.BlockHeader{
				Height: 20,
				Bits:   0,
			}},
			parent: &state.BlockNode{
				Height: 19,
				Bits:   2305843009214532812,
			},
			err: errBadBits,
		},
		{
			block: &bc.Block{BlockHeader: &bc.BlockHeader{
				Height:          20,
				PreviousBlockId: &bc.Hash{V0: 18},
			}},
			parent: &state.BlockNode{
				Height: 19,
				Hash:   bc.Hash{V0: 19},
			},
			err: errMismatchedBlock,
		},
		{
			block: &bc.Block{
				ID: bc.Hash{V0: 0},
				BlockHeader: &bc.BlockHeader{
					Height:          1,
					Timestamp:       1523352601,
					PreviousBlockId: &bc.Hash{V0: 0},
					Bits:            2305843009214532812,
				},
			},
			parent: &state.BlockNode{
				Height:    0,
				Timestamp: 1523352600,
				Hash:      bc.Hash{V0: 0},
				Seed:      &bc.Hash{V1: 1},
				Bits:      2305843009214532812,
			},
			err: errWorkProof,
		},
		{
			block: &bc.Block{
				ID: bc.Hash{V0: 1},
				BlockHeader: &bc.BlockHeader{
					Height:          1,
					Timestamp:       1523352601,
					PreviousBlockId: &bc.Hash{V0: 0},
					Bits:            2305843009214532812,
				},
			},
			parent: &state.BlockNode{
				Height:    0,
				Timestamp: 1523352600,
				Hash:      bc.Hash{V0: 0},
				Seed:      &bc.Hash{V1: 1},
				Bits:      2305843009214532812,
			},
			err: nil,
		},
	}

	for i, c := range cases {
		if err := ValidateBlockHeader(c.block, c.parent); rootErr(err) != c.err {
			t.Errorf("case %d got error %s, want %s", i, err, c.err)
		}
	}
}

func TestValidateMerkleRoot(t *testing.T) {
	// add (hash, seed) --> (tensority hash) to the  tensority cache for avoid
	// real matrix calculate cost.
	tensority.AIHash.AddCache(&bc.Hash{V0: 0}, &bc.Hash{}, testutil.MaxHash)
	tensority.AIHash.AddCache(&bc.Hash{V0: 1}, &bc.Hash{}, testutil.MinHash)
	tensority.AIHash.AddCache(&bc.Hash{V0: 1}, consensus.InitialSeed, testutil.MinHash)

	cp, _ := vmutil.DefaultCoinbaseProgram()
	cases := []struct {
		desc   string
		block  *bc.Block
		parent *state.BlockNode
		err    error
	}{
		{
			desc: "The calculated transaction merkel root hash is not equals to the hash of the block header",
			block: &bc.Block{
				ID: bc.Hash{V0: 1},
				BlockHeader: &bc.BlockHeader{
					Height:          1,
					Timestamp:       1523352601,
					PreviousBlockId: &bc.Hash{V0: 0},
					Bits:            2305843009214532812,
					TransactionsRoot: &bc.Hash{V0: 1},
				},
				Transactions: []*bc.Tx{
					types.MapTx(&types.TxData{
						SerializedSize: 1,
						Inputs:  []*types.TxInput{types.NewCoinbaseInput(nil)},
						Outputs: []*types.TxOutput{types.NewTxOutput(*consensus.BTMAssetID, 41250000000, cp)},
					}),
				},
			},
			parent: &state.BlockNode{
				Height:    0,
				Timestamp: 1523352600,
				Hash:      bc.Hash{V0: 0},
				Seed:      &bc.Hash{V1: 1},
				Bits:      2305843009214532812,
			},
			err: errMismatchedMerkleRoot,
		},
		{
			desc: "The calculated transaction status merkel root hash is not equals to the hash of the block header",
			block: &bc.Block{
				ID: bc.Hash{V0: 1},
				BlockHeader: &bc.BlockHeader{
					Height:          1,
					Timestamp:       1523352601,
					PreviousBlockId: &bc.Hash{V0: 0},
					Bits:            2305843009214532812,
					TransactionsRoot: &bc.Hash{V0: 6294987741126419124, V1: 12520373106916389157, V2: 5040806596198303681, V3: 1151748423853876189},
					TransactionStatusHash: &bc.Hash{V0: 1},
				},
				Transactions: []*bc.Tx{
					types.MapTx(&types.TxData{
						SerializedSize: 1,
						Inputs:  []*types.TxInput{types.NewCoinbaseInput(nil)},
						Outputs: []*types.TxOutput{types.NewTxOutput(*consensus.BTMAssetID, 41250000000, cp)},
					}),
				},
			},
			parent: &state.BlockNode{
				Height:    0,
				Timestamp: 1523352600,
				Hash:      bc.Hash{V0: 0},
				Seed:      &bc.Hash{V1: 1},
				Bits:      2305843009214532812,
			},
			err: errMismatchedMerkleRoot,
		},
	}

	for i, c := range cases {
		err := ValidateBlock(c.block, c.parent)
		if rootErr(err) != c.err {
			t.Errorf("case #%d (%s) got error %s, want %s", i, c.desc, err, c.err)
		}
	}
}
<|endoftext|>


<commit_before>package xmmsclient

import (
	"encoding/binary"
	"fmt"
	"io"
	"math"
)

type listConsumer func(value XmmsValue)

func deserializeInt(r io.Reader) (value XmmsInt, err error) {
	err = binary.Read(r, binary.BigEndian, &value)
	return
}

func deserializeFloat(r io.Reader) (value XmmsFloat, err error) {
	var mantissaInt int32
	var exponent int32
	var mantissa float64

	err = binary.Read(r, binary.BigEndian, &mantissaInt)
	if err != nil {
		return
	}

	err = binary.Read(r, binary.BigEndian, &exponent)
	if err != nil {
		return
	}

	if mantissaInt > 0 {
		mantissa = float64(mantissaInt) / float64(math.MaxInt32)
	} else {
		mantissa = float64(mantissaInt) / math.Abs(math.MinInt32)
	}

	value = XmmsFloat(math.Ldexp(mantissa, int(exponent)))

	return
}

func deserializeRawString(r io.Reader) (value string, err error) {
	var length uint32
	err = binary.Read(r, binary.BigEndian, &length)
	if err != nil {
		return
	}
	data := make([]byte, length)
	err = binary.Read(r, binary.BigEndian, &data)
	if err != nil {
		return
	}
	value = string(data[:length-1])

	return
}

func deserializeString(r io.Reader) (value XmmsString, err error) {
	data, err := deserializeRawString(r)
	if err == nil {
		value = XmmsString(data)
	}
	return
}

func deserializeError(r io.Reader) (value XmmsError, err error) {
	data, err := deserializeRawString(r)
	if err == nil {
		value = XmmsError(data)
	}
	return
}

func deserializeAnyList(r io.Reader, consumer listConsumer) error {
	var restrict uint32
	err := binary.Read(r, binary.BigEndian, &restrict) // TODO: respect restrict
	if err != nil {
		return err
	}
	var length uint32
	err = binary.Read(r, binary.BigEndian, &length)
	if err != nil {
		return err
	}
	if restrict != TypeNone {
		for i := uint32(0); i < length; i++ {
			entry, err := deserializeXmmsValueOfType(restrict, r)
			if err != nil {
				return err
			}
			consumer(entry)
		}
	} else {
		for i := uint32(0); i < length; i++ {
			entry, err := deserializeXmmsValue(r)
			if err != nil {
				return err
			}
			consumer(entry)
		}
	}
	return nil
}

func deserializeList(r io.Reader) (value XmmsList, err error) {
	list := XmmsList{}
	err = deserializeAnyList(r, func(value XmmsValue) {
		list = append(list, value)
	})
	value = list
	return
}

func deserializeDict(r io.Reader) (value XmmsDict, err error) {
	var length uint32
	err = binary.Read(r, binary.BigEndian, &length)
	if err != nil {
		return
	}
	dict := XmmsDict{}
	for i := uint32(0); i < length; i++ {
		var entry XmmsValue
		var key string

		key, err = deserializeRawString(r)
		if err != nil {
			return
		}

		entry, err = deserializeXmmsValue(r)
		if err != nil {
			return
		}

		dict[key] = entry
	}
	value = dict
	return
}

func deserializeColl(r io.Reader) (result XmmsColl, err error) {
	err = binary.Read(r, binary.BigEndian, &result.Type)
	if err != nil {
		return
	}

	result.Attributes, err = deserializeDict(r)
	if err != nil {
		return
	}

	err = deserializeAnyList(r, func(raw XmmsValue) {
		if value, ok := raw.(XmmsInt); ok {
			result.IDList = append(result.IDList, int(value))
		}
	})
	if err != nil {
		return
	}

	err = deserializeAnyList(r, func(raw XmmsValue) {
		if value, ok := raw.(XmmsColl); ok {
			result.Operands = append(result.Operands, value)
		}
	})

	return
}

func deserializeXmmsValueOfType(valueType uint32, r io.Reader) (result XmmsValue, err error) {
	switch valueType {
	case TypeInt64:
		result, err = deserializeInt(r)
	case TypeFloat:
		result, err = deserializeFloat(r)
	case TypeError:
		result, err = deserializeError(r)
	case TypeString:
		result, err = deserializeString(r)
	case TypeList:
		result, err = deserializeList(r)
	case TypeDict:
		result, err = deserializeDict(r)
	case TypeColl:
		result, err = deserializeColl(r)
	}
	return
}

func deserializeXmmsValue(r io.Reader) (XmmsValue, error) {
	var valueType uint32
	err := binary.Read(r, binary.BigEndian, &valueType)
	if err != nil {
		return nil, err
	}
	return deserializeXmmsValueOfType(valueType, r)
}

func tryDeserialize(r io.Reader) (XmmsValue, error) {
	value, err := deserializeXmmsValue(r)
	if err != nil {
		return nil, err
	}

	errorMessage, ok := value.(XmmsError)
	if ok {
		return nil, fmt.Errorf("%s", errorMessage)
	}

	return value, nil
}

func tryDeserializeList(r io.Reader, consumer listConsumer) error {
	var valueType uint32

	err := binary.Read(r, binary.BigEndian, &valueType)
	if err != nil {
		return err
	}

	switch valueType {
	case TypeError:
		value, err := deserializeError(r)
		if err != nil {
			return err
		}
		return fmt.Errorf("%s", value)
	case TypeList:
		return deserializeAnyList(r, consumer)
	default:
		return fmt.Errorf("Trying to parse non-list as list (%v)", valueType)
	}
}

func tryDeserializeIntList(r io.Reader) ([]int, error) {
	var list []int
	err := tryDeserializeList(r, func(raw XmmsValue) {
		if value, ok := raw.(XmmsInt); ok {
			list = append(list, int(value))
		}
	})
	if err != nil {
		return nil, err
	}
	return list, nil
}

func tryDeserializeStringList(r io.Reader) ([]string, error) {
	var list []string
	err := tryDeserializeList(r, func(raw XmmsValue) {
		if value, ok := raw.(XmmsString); ok {
			list = append(list, string(value))
		}
	})
	if err != nil {
		return nil, err
	}
	return list, nil
}

func tryDeserializeDictList(r io.Reader) ([]XmmsDict, error) {
	var list []XmmsDict
	err := tryDeserializeList(r, func(raw XmmsValue) {
		if value, ok := raw.(XmmsDict); ok {
			list = append(list, value)
		}
	})
	if err != nil {
		return nil, err
	}
	return list, nil
}
<commit_msg>Always initialize the deserialization lists.<commit_after>package xmmsclient

import (
	"encoding/binary"
	"fmt"
	"io"
	"math"
)

type listConsumer func(value XmmsValue)

func deserializeInt(r io.Reader) (value XmmsInt, err error) {
	err = binary.Read(r, binary.BigEndian, &value)
	return
}

func deserializeFloat(r io.Reader) (value XmmsFloat, err error) {
	var mantissaInt int32
	var exponent int32
	var mantissa float64

	err = binary.Read(r, binary.BigEndian, &mantissaInt)
	if err != nil {
		return
	}

	err = binary.Read(r, binary.BigEndian, &exponent)
	if err != nil {
		return
	}

	if mantissaInt > 0 {
		mantissa = float64(mantissaInt) / float64(math.MaxInt32)
	} else {
		mantissa = float64(mantissaInt) / math.Abs(math.MinInt32)
	}

	value = XmmsFloat(math.Ldexp(mantissa, int(exponent)))

	return
}

func deserializeRawString(r io.Reader) (value string, err error) {
	var length uint32
	err = binary.Read(r, binary.BigEndian, &length)
	if err != nil {
		return
	}
	data := make([]byte, length)
	err = binary.Read(r, binary.BigEndian, &data)
	if err != nil {
		return
	}
	value = string(data[:length-1])

	return
}

func deserializeString(r io.Reader) (value XmmsString, err error) {
	data, err := deserializeRawString(r)
	if err == nil {
		value = XmmsString(data)
	}
	return
}

func deserializeError(r io.Reader) (value XmmsError, err error) {
	data, err := deserializeRawString(r)
	if err == nil {
		value = XmmsError(data)
	}
	return
}

func deserializeAnyList(r io.Reader, consumer listConsumer) error {
	var restrict uint32
	err := binary.Read(r, binary.BigEndian, &restrict) // TODO: respect restrict
	if err != nil {
		return err
	}
	var length uint32
	err = binary.Read(r, binary.BigEndian, &length)
	if err != nil {
		return err
	}
	if restrict != TypeNone {
		for i := uint32(0); i < length; i++ {
			entry, err := deserializeXmmsValueOfType(restrict, r)
			if err != nil {
				return err
			}
			consumer(entry)
		}
	} else {
		for i := uint32(0); i < length; i++ {
			entry, err := deserializeXmmsValue(r)
			if err != nil {
				return err
			}
			consumer(entry)
		}
	}
	return nil
}

func deserializeList(r io.Reader) (value XmmsList, err error) {
	list := XmmsList{}
	err = deserializeAnyList(r, func(value XmmsValue) {
		list = append(list, value)
	})
	value = list
	return
}

func deserializeDict(r io.Reader) (value XmmsDict, err error) {
	var length uint32
	err = binary.Read(r, binary.BigEndian, &length)
	if err != nil {
		return
	}
	dict := XmmsDict{}
	for i := uint32(0); i < length; i++ {
		var entry XmmsValue
		var key string

		key, err = deserializeRawString(r)
		if err != nil {
			return
		}

		entry, err = deserializeXmmsValue(r)
		if err != nil {
			return
		}

		dict[key] = entry
	}
	value = dict
	return
}

func deserializeColl(r io.Reader) (result XmmsColl, err error) {
	err = binary.Read(r, binary.BigEndian, &result.Type)
	if err != nil {
		return
	}

	result.Attributes, err = deserializeDict(r)
	if err != nil {
		return
	}

	err = deserializeAnyList(r, func(raw XmmsValue) {
		if value, ok := raw.(XmmsInt); ok {
			result.IDList = append(result.IDList, int(value))
		}
	})
	if err != nil {
		return
	}

	err = deserializeAnyList(r, func(raw XmmsValue) {
		if value, ok := raw.(XmmsColl); ok {
			result.Operands = append(result.Operands, value)
		}
	})

	return
}

func deserializeXmmsValueOfType(valueType uint32, r io.Reader) (result XmmsValue, err error) {
	switch valueType {
	case TypeInt64:
		result, err = deserializeInt(r)
	case TypeFloat:
		result, err = deserializeFloat(r)
	case TypeError:
		result, err = deserializeError(r)
	case TypeString:
		result, err = deserializeString(r)
	case TypeList:
		result, err = deserializeList(r)
	case TypeDict:
		result, err = deserializeDict(r)
	case TypeColl:
		result, err = deserializeColl(r)
	}
	return
}

func deserializeXmmsValue(r io.Reader) (XmmsValue, error) {
	var valueType uint32
	err := binary.Read(r, binary.BigEndian, &valueType)
	if err != nil {
		return nil, err
	}
	return deserializeXmmsValueOfType(valueType, r)
}

func tryDeserialize(r io.Reader) (XmmsValue, error) {
	value, err := deserializeXmmsValue(r)
	if err != nil {
		return nil, err
	}

	errorMessage, ok := value.(XmmsError)
	if ok {
		return nil, fmt.Errorf("%s", errorMessage)
	}

	return value, nil
}

func tryDeserializeList(r io.Reader, consumer listConsumer) error {
	var valueType uint32

	err := binary.Read(r, binary.BigEndian, &valueType)
	if err != nil {
		return err
	}

	switch valueType {
	case TypeError:
		value, err := deserializeError(r)
		if err != nil {
			return err
		}
		return fmt.Errorf("%s", value)
	case TypeList:
		return deserializeAnyList(r, consumer)
	default:
		return fmt.Errorf("Trying to parse non-list as list (%v)", valueType)
	}
}

func tryDeserializeIntList(r io.Reader) ([]int, error) {
	list := []int{}
	err := tryDeserializeList(r, func(raw XmmsValue) {
		if value, ok := raw.(XmmsInt); ok {
			list = append(list, int(value))
		}
	})
	if err != nil {
		return nil, err
	}
	return list, nil
}

func tryDeserializeStringList(r io.Reader) ([]string, error) {
	list := []string{}
	err := tryDeserializeList(r, func(raw XmmsValue) {
		if value, ok := raw.(XmmsString); ok {
			list = append(list, string(value))
		}
	})
	if err != nil {
		return nil, err
	}
	return list, nil
}

func tryDeserializeDictList(r io.Reader) ([]XmmsDict, error) {
	list := []XmmsDict{}
	err := tryDeserializeList(r, func(raw XmmsValue) {
		if value, ok := raw.(XmmsDict); ok {
			list = append(list, value)
		}
	})
	if err != nil {
		return nil, err
	}
	return list, nil
}
<|endoftext|>


<commit_before>package mpredis

import (
	"testing"
	"time"

	"github.com/garyburd/redigo/redis"
	"github.com/soh335/go-test-redisserver"
)

var metrics = []string{
	"instantaneous_ops_per_sec", "total_connections_received", "rejected_connections", "connected_clients",
	"blocked_clients", "connected_slaves", "keys", "expires", "expired", "keyspace_hits", "keyspace_misses", "used_memory",
	"used_memory_rss", "used_memory_peak", "used_memory_lua",
}

func TestFetchMetricsUnixSocket(t *testing.T) {
	s, err := redistest.NewServer(true, nil)
	if err != nil {
		t.Errorf("Failed to invoke testserver. %s", err)
		return
	}
	defer s.Stop()

	// set test data
	conn, err := redis.Dial("unix", s.Config["unixsocket"])
	if err != nil {
		t.Errorf("Failed to create a testclient. %s", err)
		return
	}
	_, err = conn.Do("SET", "TEST_KEY1", 1)
	if err != nil {
		t.Errorf("Failed to send a SET command. %s", err)
		return
	}
	_, err = conn.Do("SET", "TEST_KEY2", 1, "EX", 1)
	if err != nil {
		t.Errorf("Failed to send a SET command. %s", err)
		return
	}
	_, err = conn.Do("SET", "TEST_KEY3", 1, "EX", 10)
	if err != nil {
		t.Errorf("Failed to send a SET command. %s", err)
		return
	}
	time.Sleep(2 * time.Second)

	redis := RedisPlugin{
		Timeout: 5,
		Prefix:  "redis",
		Socket:  s.Config["unixsocket"],
	}
	stat, err := redis.FetchMetrics()

	if err != nil {
		t.Errorf("something went wrong")
	}

	for _, v := range metrics {
		value, ok := stat[v]
		if !ok {
			t.Errorf("metric of %s cannot be fetched", v)
		}
		if v == "keys" && value != 2.0 {
			t.Errorf("metric of key should be 2, but %v", value)
		}
		if v == "expires" && value != 1.0 {
			t.Errorf("metric of expires should be 1, but %v", value)
		}
		if v == "expired" && value != 1.0 {
			t.Errorf("metric of expired should be 1, but %v", value)
		}
	}
}

func TestFetchMetricsPercentageOfMemory(t *testing.T) {
	s, err := redistest.NewServer(true, nil)
	if err != nil {
		t.Errorf("Failed to invoke testserver. %s", err)
		return
	}
	defer s.Stop()

	rp := RedisPlugin{
		Timeout: 5,
		Prefix:  "redis",
		Socket:  s.Config["unixsocket"],
	}

	conn, err := redis.Dial("unix", s.Config["unixsocket"])

	// Without maxmemory
	_, err = conn.Do("CONFIG", "SET", "maxmemory", 0.0)
	if err != nil {
		t.Errorf("Failed to send a CONFIG command. %s", err)
		return
	}

	stat1, err := rp.FetchMetrics()
	if err != nil {
		t.Errorf("something went wrong")
	}

	if value, ok := stat1["percentage_of_memory"]; !ok {
		t.Errorf("metric of 'percentage_of_memory' cannnot be fetched")
	} else if value != 0.0 {
		t.Errorf("metric of 'percentage_of_memory' should be 0.0, but %v", value)
	}

	// With maxmemory
	_, err = conn.Do("CONFIG", "SET", "maxmemory", 1024*1024)
	if err != nil {
		t.Errorf("Failed to send a CONFIG command. %s", err)
		return
	}

	stat2, err := rp.FetchMetrics()
	if err != nil {
		t.Errorf("something went wrong")
	}

	if value, ok := stat2["percentage_of_memory"]; !ok {
		t.Errorf("metric of 'percentage_of_memory' cannnot be fetched")
	} else if value == 0.0 {
		t.Errorf("metric of 'percentage_of_memory' should not be 0.0, but %v", value)
	}
}

func TestFetchMetrics(t *testing.T) {
	// should detect empty port
	portStr := "63331"
	s, err := redistest.NewServer(true, map[string]string{
		"port": portStr,
	})
	if err != nil {
		t.Errorf("Failed to invoke testserver. %s", err)
		return
	}
	defer s.Stop()
	redis := RedisPlugin{
		Timeout: 5,
		Prefix:  "redis",
		Port:    portStr,
	}
	stat, err := redis.FetchMetrics()

	if err != nil {
		t.Errorf("something went wrong")
	}

	for _, v := range metrics {
		if _, ok := stat[v]; !ok {
			t.Errorf("metric of %s cannot be fetched", v)
		}
	}
}
<commit_msg>[redis] improve the tests for expired keys<commit_after>package mpredis

import (
	"testing"
	"time"

	"github.com/garyburd/redigo/redis"
	"github.com/soh335/go-test-redisserver"
)

var metrics = []string{
	"instantaneous_ops_per_sec", "total_connections_received", "rejected_connections", "connected_clients",
	"blocked_clients", "connected_slaves", "keys", "expires", "expired", "keyspace_hits", "keyspace_misses", "used_memory",
	"used_memory_rss", "used_memory_peak", "used_memory_lua",
}

func TestFetchMetricsUnixSocket(t *testing.T) {
	s, err := redistest.NewServer(true, nil)
	if err != nil {
		t.Errorf("Failed to invoke testserver. %s", err)
		return
	}
	defer s.Stop()

	// set test data
	conn, err := redis.Dial("unix", s.Config["unixsocket"])
	if err != nil {
		t.Errorf("Failed to create a testclient. %s", err)
		return
	}
	_, err = conn.Do("SET", "TEST_KEY0", 1)
	if err != nil {
		t.Errorf("Failed to send a SET command. %s", err)
		return
	}
	conn.Do("SET", "TEST_KEY1", 1, "EX", 1)
	conn.Do("SET", "TEST_KEY2", 1, "EX", 2)
	conn.Do("SET", "TEST_KEY3", 1, "EX", 10)
	conn.Do("SET", "TEST_KEY4", 1, "EX", 20)
	conn.Do("SET", "TEST_KEY5", 1, "EX", 30)
	time.Sleep(3 * time.Second)

	redis := RedisPlugin{
		Timeout: 5,
		Prefix:  "redis",
		Socket:  s.Config["unixsocket"],
	}
	stat, err := redis.FetchMetrics()

	if err != nil {
		t.Errorf("something went wrong")
	}

	for _, v := range metrics {
		value, ok := stat[v]
		if !ok {
			t.Errorf("metric of %s cannot be fetched", v)
		}
		if v == "keys" && value != 4.0 {
			t.Errorf("metric of key should be 4, but %v", value)
		}
		if v == "expires" && value != 3.0 {
			t.Errorf("metric of expires should be 3, but %v", value)
		}
		if v == "expired" && value != 2.0 {
			t.Errorf("metric of expired should be 2, but %v", value)
		}
	}
}

func TestFetchMetricsPercentageOfMemory(t *testing.T) {
	s, err := redistest.NewServer(true, nil)
	if err != nil {
		t.Errorf("Failed to invoke testserver. %s", err)
		return
	}
	defer s.Stop()

	rp := RedisPlugin{
		Timeout: 5,
		Prefix:  "redis",
		Socket:  s.Config["unixsocket"],
	}

	conn, err := redis.Dial("unix", s.Config["unixsocket"])

	// Without maxmemory
	_, err = conn.Do("CONFIG", "SET", "maxmemory", 0.0)
	if err != nil {
		t.Errorf("Failed to send a CONFIG command. %s", err)
		return
	}

	stat1, err := rp.FetchMetrics()
	if err != nil {
		t.Errorf("something went wrong")
	}

	if value, ok := stat1["percentage_of_memory"]; !ok {
		t.Errorf("metric of 'percentage_of_memory' cannnot be fetched")
	} else if value != 0.0 {
		t.Errorf("metric of 'percentage_of_memory' should be 0.0, but %v", value)
	}

	// With maxmemory
	_, err = conn.Do("CONFIG", "SET", "maxmemory", 1024*1024)
	if err != nil {
		t.Errorf("Failed to send a CONFIG command. %s", err)
		return
	}

	stat2, err := rp.FetchMetrics()
	if err != nil {
		t.Errorf("something went wrong")
	}

	if value, ok := stat2["percentage_of_memory"]; !ok {
		t.Errorf("metric of 'percentage_of_memory' cannnot be fetched")
	} else if value == 0.0 {
		t.Errorf("metric of 'percentage_of_memory' should not be 0.0, but %v", value)
	}
}

func TestFetchMetrics(t *testing.T) {
	// should detect empty port
	portStr := "63331"
	s, err := redistest.NewServer(true, map[string]string{
		"port": portStr,
	})
	if err != nil {
		t.Errorf("Failed to invoke testserver. %s", err)
		return
	}
	defer s.Stop()
	redis := RedisPlugin{
		Timeout: 5,
		Prefix:  "redis",
		Port:    portStr,
	}
	stat, err := redis.FetchMetrics()

	if err != nil {
		t.Errorf("something went wrong")
	}

	for _, v := range metrics {
		if _, ok := stat[v]; !ok {
			t.Errorf("metric of %s cannot be fetched", v)
		}
	}
}
<|endoftext|>


<commit_before>// Copyright (c) 2015, Ben Morgan. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.

package csv

import (
	"bytes"
	"errors"
	"fmt"
	"reflect"
)

type Marshaler interface {
	MarshalCSV() ([]byte, error)
}

type Recorder interface {
	Header() []string
	Record() []string
}

var (
	marshalerType = reflect.TypeOf(new(Marshaler)).Elem()
	recorderType  = reflect.TypeOf(new(Recorder)).Elem()
)

func Marshal(v interface{}) ([]byte, error) {
	vt := reflect.TypeOf(v)

	// Check right away if it implements Marshaler or Recorder.
	if vt.Implements(marshalerType) {
		t := v.(Marshaler)
		return t.MarshalCSV()
	}
	if vt.Implements(recorderType) {
		t := v.(Recorder)
		return marshalRecorder(t), nil
	}

	// Any of the other checks only make sense on non-pointers.
	if vt.Kind() == reflect.Ptr {
		return Marshal(reflect.ValueOf(v).Elem().Interface())
	}

	// We also support a slice or array of Recorder, but not of MarshalCSV, because
	// semantically, anything could be in MarshalCSV, especially the header, and
	// we don't want to try to guess.
	if vt.Kind() == reflect.Slice || vt.Kind() == reflect.Array {
		// Even if the slice has element type *Type, the following should still work.
		if vt.Elem().Implements(recorderType) {
			return marshalRecorderSlice(v)
		}

		// We might get a slice of some kind of interface, in which case we require that
		// each element is of the same type.
		if vt.Elem().Kind() == reflect.Interface {
			return marshalInterfaceSlice(v)
		}

		return nil, fmt.Errorf("csv: slice element type %s does not implement Recorder", vt.Elem())
	}

	return nil, fmt.Errorf("csv: cannot marshal %s", vt)
}

func marshalRecorder(v Recorder) []byte {
	var buf bytes.Buffer
	writeRecord(&buf, v.Header())
	writeRecord(&buf, v.Record())
	return buf.Bytes()
}

func marshalRecorderSlice(v interface{}) ([]byte, error) {
	vv := reflect.ValueOf(v)
	n := vv.Len()
	if n == 0 {
		return nil, errors.New("csv: no data")
	}

	get := func(i int) Recorder {
		return vv.Index(i).Interface().(Recorder)
	}

	var buf bytes.Buffer
	writeRecord(&buf, get(0).Header())
	for i := 0; i < n; i++ {
		writeRecord(&buf, get(i).Record())
	}
	return buf.Bytes(), nil
}

// marshalInterfaceSlice takes a slice or array of an interface type.
//
// We require that each type in the slice/array is of the same type.
func marshalInterfaceSlice(v interface{}) (bs []byte, err error) {
	vv := reflect.ValueOf(v)
	n := vv.Len()
	if n == 0 {
		return nil, errors.New("csv: no data")
	}

	t := reflect.TypeOf(vv.Index(0).Interface())
	get := func(i int) Recorder {
		r, ok := vv.Index(i).Interface().(Recorder)
		if !ok {
			panic(fmt.Errorf("csv: slice element %T does not implement Recorder", vv.Index(i).Interface()))
		}
		if rt := reflect.TypeOf(r); rt != t {
			panic(fmt.Errorf("csv: expecting slice element type %s, got %s", t, rt))
		}
		return r
	}

	defer func() {
		if r := recover(); r != nil {
			if e, ok := r.(error); ok {
				err = e
				return
			}
			// It's not an error, so continue panicking.
			panic(r)
		}
	}()

	var buf bytes.Buffer
	writeRecord(&buf, get(0).Header())
	for i := 0; i < n; i++ {
		writeRecord(&buf, get(i).Record())
	}
	return buf.Bytes(), nil
}

func writeRecord(buf *bytes.Buffer, slice []string) {
	m := len(slice) - 1
	for _, s := range slice[:m] {
		buf.WriteString(s)
		buf.WriteRune(',')
	}
	buf.WriteString(slice[m])
	buf.WriteRune('\n')
}
<commit_msg>Improving package documentation<commit_after>// Copyright (c) 2015, Ben Morgan. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.

package csv

import (
	"bytes"
	"errors"
	"fmt"
	"reflect"
)

// A Marshaler specifies directly how the data is marshaled to CSV.
// The result should include the header as well as the data.
type Marshaler interface {
	MarshalCSV() ([]byte, error)
}

// A Recorder specifies the Header that a file returns, as well as the string
// representation of the values. The lengths of elements returned by Header()
// and Record() should be the same.
//
// The reason for implementing Recorder is that a slice or array of Recorder
// can be marshaled.
type Recorder interface {
	Header() []string
	Record() []string
}

var (
	marshalerType = reflect.TypeOf(new(Marshaler)).Elem()
	recorderType  = reflect.TypeOf(new(Recorder)).Elem()
)

// Marshal takes a type that implements Marshaler, Recorder, or a slice or array
// of types that implement Recorder and returns a CSV byte slice.
//
// Even a slice of interface{} can be marshaled, provided that the type of every
// value in the slice is the same, and each type implements Recorder.
func Marshal(v interface{}) ([]byte, error) {
	vt := reflect.TypeOf(v)

	// Check right away if it implements Marshaler or Recorder.
	if vt.Implements(marshalerType) {
		t := v.(Marshaler)
		return t.MarshalCSV()
	}
	if vt.Implements(recorderType) {
		t := v.(Recorder)
		return marshalRecorder(t), nil
	}

	// Any of the other checks only make sense on non-pointers.
	if vt.Kind() == reflect.Ptr {
		return Marshal(reflect.ValueOf(v).Elem().Interface())
	}

	// We also support a slice or array of Recorder, but not of MarshalCSV, because
	// semantically, anything could be in MarshalCSV, especially the header, and
	// we don't want to try to guess.
	if vt.Kind() == reflect.Slice || vt.Kind() == reflect.Array {
		// Even if the slice has element type *Type, the following should still work.
		if vt.Elem().Implements(recorderType) {
			return marshalRecorderSlice(v)
		}

		// We might get a slice of some kind of interface, in which case we require that
		// each element is of the same type.
		if vt.Elem().Kind() == reflect.Interface {
			return marshalInterfaceSlice(v)
		}

		return nil, fmt.Errorf("csv: slice element type %s does not implement Recorder", vt.Elem())
	}

	return nil, fmt.Errorf("csv: cannot marshal %s", vt)
}

func marshalRecorder(v Recorder) []byte {
	var buf bytes.Buffer
	writeRecord(&buf, v.Header())
	writeRecord(&buf, v.Record())
	return buf.Bytes()
}

func marshalRecorderSlice(v interface{}) ([]byte, error) {
	vv := reflect.ValueOf(v)
	n := vv.Len()
	if n == 0 {
		return nil, errors.New("csv: no data")
	}

	get := func(i int) Recorder {
		return vv.Index(i).Interface().(Recorder)
	}

	var buf bytes.Buffer
	writeRecord(&buf, get(0).Header())
	for i := 0; i < n; i++ {
		writeRecord(&buf, get(i).Record())
	}
	return buf.Bytes(), nil
}

// marshalInterfaceSlice takes a slice or array of an interface type.
//
// We require that each type in the slice/array is of the same type.
func marshalInterfaceSlice(v interface{}) (bs []byte, err error) {
	vv := reflect.ValueOf(v)
	n := vv.Len()
	if n == 0 {
		return nil, errors.New("csv: no data")
	}

	t := reflect.TypeOf(vv.Index(0).Interface())
	get := func(i int) Recorder {
		r, ok := vv.Index(i).Interface().(Recorder)
		if !ok {
			panic(fmt.Errorf("csv: slice element %T does not implement Recorder", vv.Index(i).Interface()))
		}
		if rt := reflect.TypeOf(r); rt != t {
			panic(fmt.Errorf("csv: expecting slice element type %s, got %s", t, rt))
		}
		return r
	}

	defer func() {
		if r := recover(); r != nil {
			if e, ok := r.(error); ok {
				err = e
				return
			}
			// It's not an error, so continue panicking.
			panic(r)
		}
	}()

	var buf bytes.Buffer
	writeRecord(&buf, get(0).Header())
	for i := 0; i < n; i++ {
		writeRecord(&buf, get(i).Record())
	}
	return buf.Bytes(), nil
}

func writeRecord(buf *bytes.Buffer, slice []string) {
	m := len(slice) - 1
	for _, s := range slice[:m] {
		buf.WriteString(s)
		buf.WriteRune(',')
	}
	buf.WriteString(slice[m])
	buf.WriteRune('\n')
}
<|endoftext|>


<commit_before>package ctx

import (
	"context"
	"sync"
	"time"
)

// Doner can block until something is done
type Doner interface {
	Done() <-chan struct{}
}

// DoneChan is a basic implementation of Doner
type DoneChan <-chan struct{}

// Done returns a channel that receives when an action is complete
func (dc DoneChan) Done() <-chan struct{} { return dc }

// Lift takes a chan and wraps it in a Doner
func Lift(c <-chan struct{}) Doner { return DoneChan(c) }

// AsContext creates a context that fires when the Doner fires
func AsContext(d Doner) context.Context {
	c, cancel := context.WithCancel(context.Background())
	Defer(d, cancel)
	return c
}

// WithCancel returns a new Doner that can be cancelled via the associated
// function
func WithCancel(d Doner) (Doner, func()) {
	cq := make(chan struct{})
	return Lift(cq), func() { close(cq) }
}

// Tick returns a <-chan whose range ends when the underlying context cancels
func Tick(d Doner) <-chan struct{} {
	cq := make(chan struct{})
	go func() {
		for {
			select {
			case <-d.Done():
				close(cq)
				return
			case cq <- struct{}{}:
			}
		}
	}()
	return cq
}

// Defer guarantees that a function will be called after a context has cancelled
func Defer(d Doner, cb func()) {
	go func() {
		<-d.Done()
		cb()
	}()
}

// Link returns a channel that fires if ANY of the constituent Doners have fired
func Link(doners ...Doner) <-chan struct{} {
	c := make(chan struct{})
	cancel := func() { close(c) }

	var once sync.Once
	for _, d := range doners {
		Defer(d, func() { once.Do(cancel) })
	}

	return c
}

// Join returns a channel that receives when all constituent Doners have fired
func Join(doners ...Doner) <-chan struct{} {
	var wg sync.WaitGroup
	wg.Add(len(doners))
	for _, d := range doners {
		Defer(d, wg.Done)
	}

	cq := make(chan struct{})
	go func() {
		wg.Wait()
		close(cq)
	}()
	return cq
}

// FTick calls a function in a loop until the Doner has fired
func FTick(d Doner, f func()) {
	for _ = range Tick(d) {
		f()
	}
}

// FTickInterval calls a function repeatedly at a given internval, until the Doner
// has fired.
func FTickInterval(d Doner, t time.Duration, f func()) {
	for _ = range time.NewTicker(t).C {
		select {
		case <-d.Done():
			return
		default:
			f()
		}
	}
}
<commit_msg>Return DoneChan from Link and Join<commit_after>package ctx

import (
	"context"
	"sync"
	"time"
)

// Doner can block until something is done
type Doner interface {
	Done() <-chan struct{}
}

// DoneChan is a basic implementation of Doner
type DoneChan <-chan struct{}

// Done returns a channel that receives when an action is complete
func (dc DoneChan) Done() <-chan struct{} { return dc }

// Lift takes a chan and wraps it in a Doner
func Lift(c <-chan struct{}) Doner { return DoneChan(c) }

// AsContext creates a context that fires when the Doner fires
func AsContext(d Doner) context.Context {
	c, cancel := context.WithCancel(context.Background())
	Defer(d, cancel)
	return c
}

// WithCancel returns a new Doner that can be cancelled via the associated
// function
func WithCancel(d Doner) (Doner, func()) {
	cq := make(chan struct{})
	return Lift(cq), func() { close(cq) }
}

// Tick returns a <-chan whose range ends when the underlying context cancels
func Tick(d Doner) <-chan struct{} {
	cq := make(chan struct{})
	go func() {
		for {
			select {
			case <-d.Done():
				close(cq)
				return
			case cq <- struct{}{}:
			}
		}
	}()
	return cq
}

// Defer guarantees that a function will be called after a context has cancelled
func Defer(d Doner, cb func()) {
	go func() {
		<-d.Done()
		cb()
	}()
}

// Link returns a channel that fires if ANY of the constituent Doners have fired
func Link(doners ...Doner) DoneChan {
	c := make(chan struct{})
	cancel := func() { close(c) }

	var once sync.Once
	for _, d := range doners {
		Defer(d, func() { once.Do(cancel) })
	}

	return c
}

// Join returns a channel that receives when all constituent Doners have fired
func Join(doners ...Doner) DoneChan {
	var wg sync.WaitGroup
	wg.Add(len(doners))
	for _, d := range doners {
		Defer(d, wg.Done)
	}

	cq := make(chan struct{})
	go func() {
		wg.Wait()
		close(cq)
	}()
	return cq
}

// FTick calls a function in a loop until the Doner has fired
func FTick(d Doner, f func()) {
	for _ = range Tick(d) {
		f()
	}
}

// FTickInterval calls a function repeatedly at a given internval, until the Doner
// has fired.
func FTickInterval(d Doner, t time.Duration, f func()) {
	for _ = range time.NewTicker(t).C {
		select {
		case <-d.Done():
			return
		default:
			f()
		}
	}
}
<|endoftext|>


<commit_before>package config

import (
	"encoding/json"
	"io/ioutil"
)

/* General structure of our configuration */
type Config struct {
	HostCertMap     map[string]string
	HostEndpointMap map[string]Endpoint
}

/* Endpoint to connect to for a given domain name suffix */
type Endpoint struct {
	Addr string
	Port string
}

/* Function to produce an empty config map */
func EmptyConfig() *Config {
	hostEndpointMap := make(map[string]Endpoint)
	hostEndpointMap["example.com"] = Endpoint{Addr: "127.0.0.1", Port: "8080"}
	return &Config{
		HostCertMap:     map[string]string{"example.com": "./this.cert"},
		HostEndpointMap: hostEndpointMap,
	}
}

/* Serialize a Config to a []byte as json */
func (c *Config) ToJson() []byte {
	data, _ := json.MarshalIndent(c, "", "  ")
	return data
}

/* Parse a Config from a []byte as json */
func FromJson(data []byte) (*Config, error) {
	var config Config
	err := json.Unmarshal(data, &config)
	if err != nil {
		return nil, err
	}
	return &config, nil
}

/* Write a Config to a File */
func (c *Config) WriteFile(path string) error {
	return ioutil.WriteFile(path, c.ToJson(), 0555)
}

/* Read a Config from a File */
func ReadFile(path string) (*Config, error) {
	data, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}
	return FromJson(data)
}
<commit_msg>Improve config structure<commit_after>package config

import (
	"encoding/json"
	"io/ioutil"
)

/* General structure of our configuration */
type Config struct {
	CreateMissingCerts bool
	HostCertMap        map[string]CertPaths
	HostEndpointMap    map[string]Endpoint
}

/* Endpoint to connect to for a given domain name suffix */
type Endpoint struct {
	Addr string
	Port string
}

/* Description of cert file locations */
type CertPaths struct {
	Certfile string
	Keyfile  string
}

/* Function to produce an empty config map */
func EmptyConfig() *Config {
	hostCertMap := make(map[string]CertPaths)
	hostCertMap["example.com"] = CertPaths{Certfile: "./example.com.cert.pem", Keyfile: "./example.com.key.pem"}
	hostEndpointMap := make(map[string]Endpoint)
	hostEndpointMap["example.com"] = Endpoint{Addr: "127.0.0.1", Port: "8080"}
	return &Config{
		CreateMissingCerts: true,
		HostCertMap:        hostCertMap,
		HostEndpointMap:    hostEndpointMap,
	}
}

/* Serialize a Config to a []byte as json */
func (c *Config) ToJson() []byte {
	data, _ := json.MarshalIndent(c, "", "  ")
	return data
}

/* Parse a Config from a []byte as json */
func FromJson(data []byte) (*Config, error) {
	var config Config
	err := json.Unmarshal(data, &config)
	if err != nil {
		return nil, err
	}
	return &config, nil
}

/* Write a Config to a File */
func (c *Config) WriteFile(path string) error {
	return ioutil.WriteFile(path, c.ToJson(), 0555)
}

/* Read a Config from a File */
func ReadFile(path string) (*Config, error) {
	data, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}
	return FromJson(data)
}
<|endoftext|>


<commit_before>// Copyright 2015 CoreOS, Inc.
//
// 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 config

import (
	"bytes"
	"errors"

	"github.com/coreos/ignition/config/types"
	"github.com/coreos/ignition/config/v1"
	"github.com/coreos/ignition/config/validate"
	"github.com/coreos/ignition/config/validate/report"

	json "github.com/ajeddeloh/go-json"
	"go4.org/errorutil"
)

var (
	ErrCloudConfig = errors.New("not a config (found coreos-cloudconfig)")
	ErrEmpty       = errors.New("not a config (empty)")
	ErrScript      = errors.New("not a config (found coreos-cloudinit script)")
	ErrDeprecated  = errors.New("config format deprecated")
	ErrInvalid     = errors.New("config is not valid")
)

// Parse parses the raw config into a types.Config struct and generates a report of any
// errors, warnings, info, and deprecations it encountered
func Parse(rawConfig []byte) (types.Config, report.Report, error) {
	switch version(rawConfig) {
	case types.IgnitionVersion{Major: 1}:
		config, err := ParseFromV1(rawConfig)
		if err != nil {
			return types.Config{}, report.ReportFromError(err, report.EntryError), err
		}

		return config, report.ReportFromError(ErrDeprecated, report.EntryDeprecated), nil
	default:
		return ParseFromLatest(rawConfig)
	}
}

func ParseFromLatest(rawConfig []byte) (types.Config, report.Report, error) {
	if isEmpty(rawConfig) {
		return types.Config{}, report.Report{}, ErrEmpty
	} else if isCloudConfig(rawConfig) {
		return types.Config{}, report.Report{}, ErrCloudConfig
	} else if isScript(rawConfig) {
		return types.Config{}, report.Report{}, ErrScript
	}

	var err error
	var config types.Config

	// These errors are fatal and the config should not be further validated
	if err = json.Unmarshal(rawConfig, &config); err == nil {
		versionReport := config.Ignition.Version.Validate()
		if versionReport.IsFatal() {
			return types.Config{}, versionReport, ErrInvalid
		}
	}

	// Handle json syntax and type errors first, since they are fatal but have offset info
	if serr, ok := err.(*json.SyntaxError); ok {
		line, col, highlight := errorutil.HighlightBytePosition(bytes.NewReader(rawConfig), serr.Offset)
		return types.Config{},
			report.Report{
				Entries: []report.Entry{{
					Kind:      report.EntryError,
					Message:   serr.Error(),
					Line:      line,
					Column:    col,
					Highlight: highlight,
				}},
			},
			ErrInvalid
	}

	if terr, ok := err.(*json.UnmarshalTypeError); ok {
		line, col, highlight := errorutil.HighlightBytePosition(bytes.NewReader(rawConfig), terr.Offset)
		return types.Config{},
			report.Report{
				Entries: []report.Entry{{
					Kind:      report.EntryError,
					Message:   terr.Error(),
					Line:      line,
					Column:    col,
					Highlight: highlight,
				}},
			},
			ErrInvalid
	}

	// Handle other fatal errors (i.e. invalid version)
	if err != nil {
		return types.Config{}, report.ReportFromError(err, report.EntryError), err
	}

	// Unmarshal again to a json.Node to get offset information for building a report
	var ast json.Node
	var r report.Report
	if err := json.Unmarshal(rawConfig, &ast); err != nil {
		r.Add(report.Entry{
			Kind:    report.EntryWarning,
			Message: "Ignition could not unmarshal your config for reporting line numbers. This should never happen. Please file a bug.",
		})
		r.Merge(validate.ValidateWithoutSource(config))
	} else {
		r.Merge(validate.Validate(config, ast, bytes.NewReader(rawConfig)))
	}

	if r.IsFatal() {
		return types.Config{}, r, ErrInvalid
	}

	return config, r, nil
}

func ParseFromV1(rawConfig []byte) (types.Config, error) {
	config, err := v1.Parse(rawConfig)
	if err != nil {
		return types.Config{}, err
	}

	return TranslateFromV1(config), nil
}

func version(rawConfig []byte) types.IgnitionVersion {
	var composite struct {
		Version  *int `json:"ignitionVersion"`
		Ignition struct {
			Version *types.IgnitionVersion `json:"version"`
		} `json:"ignition"`
	}

	if json.Unmarshal(rawConfig, &composite) == nil {
		if composite.Ignition.Version != nil {
			return *composite.Ignition.Version
		} else if composite.Version != nil {
			return types.IgnitionVersion{Major: int64(*composite.Version)}
		}
	}

	return types.IgnitionVersion{}
}

func isEmpty(userdata []byte) bool {
	return len(userdata) == 0
}
<commit_msg>config: parse v2.0 configs seperately<commit_after>// Copyright 2015 CoreOS, Inc.
//
// 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 config

import (
	"bytes"
	"errors"

	"github.com/coreos/ignition/config/types"
	"github.com/coreos/ignition/config/v1"
	"github.com/coreos/ignition/config/v2_0"
	"github.com/coreos/ignition/config/validate"
	"github.com/coreos/ignition/config/validate/report"

	json "github.com/ajeddeloh/go-json"
	"go4.org/errorutil"
)

var (
	ErrCloudConfig = errors.New("not a config (found coreos-cloudconfig)")
	ErrEmpty       = errors.New("not a config (empty)")
	ErrScript      = errors.New("not a config (found coreos-cloudinit script)")
	ErrDeprecated  = errors.New("config format deprecated")
	ErrInvalid     = errors.New("config is not valid")
)

// Parse parses the raw config into a types.Config struct and generates a report of any
// errors, warnings, info, and deprecations it encountered
func Parse(rawConfig []byte) (types.Config, report.Report, error) {
	switch version(rawConfig) {
	case types.IgnitionVersion{Major: 1}:
		config, err := ParseFromV1(rawConfig)
		if err != nil {
			return types.Config{}, report.ReportFromError(err, report.EntryError), err
		}

		return config, report.ReportFromError(ErrDeprecated, report.EntryDeprecated), nil
	case types.IgnitionVersion{Major: 2, Minor: 0}:
		return ParseFromV2_0(rawConfig)
	default:
		return ParseFromLatest(rawConfig)
	}
}

func ParseFromLatest(rawConfig []byte) (types.Config, report.Report, error) {
	if isEmpty(rawConfig) {
		return types.Config{}, report.Report{}, ErrEmpty
	} else if isCloudConfig(rawConfig) {
		return types.Config{}, report.Report{}, ErrCloudConfig
	} else if isScript(rawConfig) {
		return types.Config{}, report.Report{}, ErrScript
	}

	var err error
	var config types.Config

	// These errors are fatal and the config should not be further validated
	if err = json.Unmarshal(rawConfig, &config); err == nil {
		versionReport := config.Ignition.Version.Validate()
		if versionReport.IsFatal() {
			return types.Config{}, versionReport, ErrInvalid
		}
	}

	// Handle json syntax and type errors first, since they are fatal but have offset info
	if serr, ok := err.(*json.SyntaxError); ok {
		line, col, highlight := errorutil.HighlightBytePosition(bytes.NewReader(rawConfig), serr.Offset)
		return types.Config{},
			report.Report{
				Entries: []report.Entry{{
					Kind:      report.EntryError,
					Message:   serr.Error(),
					Line:      line,
					Column:    col,
					Highlight: highlight,
				}},
			},
			ErrInvalid
	}

	if terr, ok := err.(*json.UnmarshalTypeError); ok {
		line, col, highlight := errorutil.HighlightBytePosition(bytes.NewReader(rawConfig), terr.Offset)
		return types.Config{},
			report.Report{
				Entries: []report.Entry{{
					Kind:      report.EntryError,
					Message:   terr.Error(),
					Line:      line,
					Column:    col,
					Highlight: highlight,
				}},
			},
			ErrInvalid
	}

	// Handle other fatal errors (i.e. invalid version)
	if err != nil {
		return types.Config{}, report.ReportFromError(err, report.EntryError), err
	}

	// Unmarshal again to a json.Node to get offset information for building a report
	var ast json.Node
	var r report.Report
	if err := json.Unmarshal(rawConfig, &ast); err != nil {
		r.Add(report.Entry{
			Kind:    report.EntryWarning,
			Message: "Ignition could not unmarshal your config for reporting line numbers. This should never happen. Please file a bug.",
		})
		r.Merge(validate.ValidateWithoutSource(config))
	} else {
		r.Merge(validate.Validate(config, ast, bytes.NewReader(rawConfig)))
	}

	if r.IsFatal() {
		return types.Config{}, r, ErrInvalid
	}

	return config, r, nil
}

func ParseFromV1(rawConfig []byte) (types.Config, error) {
	config, err := v1.Parse(rawConfig)
	if err != nil {
		return types.Config{}, err
	}

	return TranslateFromV1(config), nil
}

func ParseFromV2_0(rawConfig []byte) (types.Config, report.Report, error) {
	cfg, report, err := v2_0.Parse(rawConfig)
	if err != nil {
		return types.Config{}, report, err
	}

	return TranslateFromV2_0(cfg), report, err
}

func version(rawConfig []byte) types.IgnitionVersion {
	var composite struct {
		Version  *int `json:"ignitionVersion"`
		Ignition struct {
			Version *types.IgnitionVersion `json:"version"`
		} `json:"ignition"`
	}

	if json.Unmarshal(rawConfig, &composite) == nil {
		if composite.Ignition.Version != nil {
			return *composite.Ignition.Version
		} else if composite.Version != nil {
			return types.IgnitionVersion{Major: int64(*composite.Version)}
		}
	}

	return types.IgnitionVersion{}
}

func isEmpty(userdata []byte) bool {
	return len(userdata) == 0
}
<|endoftext|>


<commit_before>package netscaler

import (
	"github.com/chiradeep/go-nitro/config/cs"
	"github.com/chiradeep/go-nitro/netscaler"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"

	"fmt"
	"log"
)

func resourceNetScalerCspolicy() *schema.Resource {
	return &schema.Resource{
		SchemaVersion: 1,
		Create:        createCspolicyFunc,
		Read:          readCspolicyFunc,
		Update:        updateCspolicyFunc,
		Delete:        deleteCspolicyFunc,
		Schema: map[string]*schema.Schema{
			"action": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"domain": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"logaction": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"newname": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"policyname": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"rule": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"url": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},

			"csvserver": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
			},
			"targetlbvserver": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
			},
			"priority": &schema.Schema{
				Type:     schema.TypeInt,
				Optional: true,
			},
		},
	}
}

func createCspolicyFunc(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*NetScalerNitroClient).client
	var cspolicyName string
	if v, ok := d.GetOk("policyname"); ok {
		cspolicyName = v.(string)
	} else {
		cspolicyName = resource.PrefixedUniqueId("tf-cspolicy-")
		d.Set("name", cspolicyName)
	}
	cspolicy := cs.Cspolicy{
		Action:     d.Get("action").(string),
		Domain:     d.Get("domain").(string),
		Logaction:  d.Get("logaction").(string),
		Newname:    d.Get("newname").(string),
		Policyname: d.Get("policyname").(string),
		Rule:       d.Get("rule").(string),
		Url:        d.Get("url").(string),
	}

	_, err := client.AddResource(netscaler.Cspolicy.Type(), cspolicyName, &cspolicy)
	if err != nil {
		return err
	}

	d.SetId(cspolicyName)
	csvserver := d.Get("csvserver").(string)
	targetlbvserver := d.Get("targetlbvserver").(string)
	priority := d.Get("priority").(int)

	binding := cs.Csvservercspolicybinding{
		Name:            csvserver,
		Policyname:      cspolicyName,
		Targetlbvserver: targetlbvserver,
		Priority:        priority,
	}

	err = client.BindResource(netscaler.Csvserver.Type(), csvserver, netscaler.Cspolicy.Type(), cspolicyName, &binding)
	if err != nil {
		log.Printf("Failed to bind cspolicy to Csvserver")
		return err
	}
	err = readCspolicyFunc(d, meta)
	if err != nil {
		log.Printf("?? we just created this cspolicy but we can't read it ?? %s", cspolicyName)
		return nil
	}
	return nil
}

func readCspolicyFunc(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*NetScalerNitroClient).client
	cspolicyName := d.Id()
	log.Printf("Reading cspolicy state %s", cspolicyName)
	data, err := client.FindResource(netscaler.Cspolicy.Type(), cspolicyName)
	if err != nil {
		log.Printf("Clearing cspolicy state %s", cspolicyName)
		d.SetId("")
		return nil
	}
	d.Set("name", data["name"])
	d.Set("action", data["action"])
	d.Set("domain", data["domain"])
	d.Set("logaction", data["logaction"])
	d.Set("newname", data["newname"])
	d.Set("policyname", data["policyname"])
	d.Set("rule", data["rule"])
	d.Set("url", data["url"])

	return nil

}

func updateCspolicyFunc(d *schema.ResourceData, meta interface{}) error {
	log.Printf("[DEBUG] In update func")
	client := meta.(*NetScalerNitroClient).client
	cspolicyName := d.Get("policyname").(string)

	cspolicy := cs.Cspolicy{
		Policyname: d.Get("policyname").(string),
	}
	if d.HasChange("action") {
		log.Printf("[DEBUG] Action has changed for cspolicy %s, starting update", cspolicyName)
		cspolicy.Action = d.Get("action").(string)
	}
	if d.HasChange("domain") {
		log.Printf("[DEBUG] Domain has changed for cspolicy %s, starting update", cspolicyName)
		cspolicy.Domain = d.Get("domain").(string)
	}
	if d.HasChange("logaction") {
		log.Printf("[DEBUG] Logaction has changed for cspolicy %s, starting update", cspolicyName)
		cspolicy.Logaction = d.Get("logaction").(string)
	}
	if d.HasChange("newname") {
		log.Printf("[DEBUG] Newname has changed for cspolicy %s, starting update", cspolicyName)
		cspolicy.Newname = d.Get("newname").(string)
	}
	if d.HasChange("rule") {
		log.Printf("[DEBUG] Rule has changed for cspolicy %s, starting update", cspolicyName)
		cspolicy.Rule = d.Get("rule").(string)
	}
	if d.HasChange("url") {
		log.Printf("[DEBUG] Url has changed for cspolicy %s, starting update", cspolicyName)
		cspolicy.Url = d.Get("url").(string)
	}

	_, err := client.UpdateResource(netscaler.Cspolicy.Type(), cspolicyName, &cspolicy)
	if err != nil {
		return fmt.Errorf("Error updating cspolicy %s", cspolicyName)
	}
	return readCspolicyFunc(d, meta)
}

func deleteCspolicyFunc(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*NetScalerNitroClient).client
	cspolicyName := d.Id()
	csvserver := d.Get("csvserver").(string)

	//First we unbind from cs vserver if necessary
	err := client.UnbindResource(netscaler.Csvserver.Type(), csvserver, netscaler.Cspolicy.Type(), cspolicyName, "policyname")
	if err != nil {
		return fmt.Errorf("Error unbinding cspolicy from csvserver %s", cspolicyName)
	}
	err = client.DeleteResource(netscaler.Cspolicy.Type(), cspolicyName)
	if err != nil {
		return fmt.Errorf("Error  deleting cspolicy %s", cspolicyName)
	}

	d.SetId("")

	return nil
}
<commit_msg>Add some validation (priority is needed). Also, if targetlbvserver is changed then we need to unbind and rebind<commit_after>package netscaler

import (
	"github.com/chiradeep/go-nitro/config/cs"
	"github.com/chiradeep/go-nitro/netscaler"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"

	"fmt"
	"log"
)

func resourceNetScalerCspolicy() *schema.Resource {
	return &schema.Resource{
		SchemaVersion: 1,
		Create:        createCspolicyFunc,
		Read:          readCspolicyFunc,
		Update:        updateCspolicyFunc,
		Delete:        deleteCspolicyFunc,
		Schema: map[string]*schema.Schema{
			"action": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"domain": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"logaction": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"newname": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"policyname": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"rule": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"url": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},

			"csvserver": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"targetlbvserver": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
			},
			"priority": &schema.Schema{
				Type:     schema.TypeInt,
				Optional: true,
			},
		},
	}
}

func createCspolicyFunc(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*NetScalerNitroClient).client

	csvserver := d.Get("csvserver").(string)
	targetlbvserver, lbok := d.GetOk("targetlbvserver")
	priority, pok := d.GetOk("priority")
	_, aok := d.GetOk("action")

	if lbok && !pok {
		return fmt.Errorf("Priority needs to be specified if target lb vserver is specified")
	}
	if !lbok && pok {
		return fmt.Errorf("Priority needs to be specified if target lb vserver is specified")
	}
	if !lbok && !aok {
		return fmt.Errorf("Action or targetlbvserver needs to be specified")
	}

	var cspolicyName string
	if v, ok := d.GetOk("policyname"); ok {
		cspolicyName = v.(string)
	} else {
		cspolicyName = resource.PrefixedUniqueId("tf-cspolicy-")
		d.Set("name", cspolicyName)
	}
	cspolicy := cs.Cspolicy{
		Action:     d.Get("action").(string),
		Domain:     d.Get("domain").(string),
		Logaction:  d.Get("logaction").(string),
		Newname:    d.Get("newname").(string),
		Policyname: d.Get("policyname").(string),
		Rule:       d.Get("rule").(string),
		Url:        d.Get("url").(string),
	}

	_, err := client.AddResource(netscaler.Cspolicy.Type(), cspolicyName, &cspolicy)
	if err != nil {
		return err
	}

	d.SetId(cspolicyName)

	binding := cs.Csvservercspolicybinding{
		Name:            csvserver,
		Policyname:      cspolicyName,
		Targetlbvserver: targetlbvserver.(string),
		Priority:        priority.(int),
	}

	if !lbok {
		binding = cs.Csvservercspolicybinding{
			Name:       csvserver,
			Policyname: cspolicyName,
		}
	}

	err = client.BindResource(netscaler.Csvserver.Type(), csvserver, netscaler.Cspolicy.Type(), cspolicyName, &binding)
	if err != nil {
		log.Printf("Failed to bind cspolicy to Csvserver")
		return err
	}
	err = readCspolicyFunc(d, meta)
	if err != nil {
		log.Printf("?? we just created this cspolicy but we can't read it ?? %s", cspolicyName)
		return nil
	}
	return nil
}

func readCspolicyFunc(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*NetScalerNitroClient).client
	cspolicyName := d.Id()
	log.Printf("Reading cspolicy state %s", cspolicyName)
	data, err := client.FindResource(netscaler.Cspolicy.Type(), cspolicyName)
	if err != nil {
		log.Printf("Clearing cspolicy state %s", cspolicyName)
		d.SetId("")
		return nil
	}
	d.Set("name", data["name"])
	d.Set("action", data["action"])
	d.Set("domain", data["domain"])
	d.Set("logaction", data["logaction"])
	d.Set("rule", data["rule"])
	d.Set("url", data["url"])

	return nil

}

func updateCspolicyFunc(d *schema.ResourceData, meta interface{}) error {
	log.Printf("[DEBUG] In update func")
	client := meta.(*NetScalerNitroClient).client
	cspolicyName := d.Get("policyname").(string)
	csvserver := d.Get("csvserver").(string)

	cspolicy := cs.Cspolicy{
		Policyname: d.Get("policyname").(string),
	}
	hasChange := false
	lbvserverChanged := false

	if d.HasChange("action") {
		log.Printf("[DEBUG] Action has changed for cspolicy %s, starting update", cspolicyName)
		cspolicy.Action = d.Get("action").(string)
		hasChange = true
	}
	if d.HasChange("domain") {
		log.Printf("[DEBUG] Domain has changed for cspolicy %s, starting update", cspolicyName)
		cspolicy.Domain = d.Get("domain").(string)
		hasChange = true
	}
	if d.HasChange("logaction") {
		log.Printf("[DEBUG] Logaction has changed for cspolicy %s, starting update", cspolicyName)
		cspolicy.Logaction = d.Get("logaction").(string)
		hasChange = true
	}
	if d.HasChange("newname") {
		log.Printf("[DEBUG] Newname has changed for cspolicy %s, starting update", cspolicyName)
		cspolicy.Newname = d.Get("newname").(string)
		hasChange = true
	}
	if d.HasChange("rule") {
		log.Printf("[DEBUG] Rule has changed for cspolicy %s, starting update", cspolicyName)
		cspolicy.Rule = d.Get("rule").(string)
		hasChange = true
	}
	if d.HasChange("url") {
		log.Printf("[DEBUG] Url has changed for cspolicy %s, starting update", cspolicyName)
		cspolicy.Url = d.Get("url").(string)
		hasChange = true
	}

	if d.HasChange("targetlbvserver") {
		log.Printf("[DEBUG] targetlbvserver has changed for cspolicy %s, starting update", cspolicyName)
		lbvserverChanged = true
	}

	if lbvserverChanged {
		//Binding has to be updated
		//First we unbind from cs vserver
		err := client.UnbindResource(netscaler.Csvserver.Type(), csvserver, netscaler.Cspolicy.Type(), cspolicyName, "policyname")
		if err != nil {
			return fmt.Errorf("Error unbinding cspolicy from csvserver %s", cspolicyName)
		}
		log.Printf("[DEBUG] cspolicy has been unbound from csvserver for cspolicy %s ", cspolicyName)
	}

	if hasChange {
		_, err := client.UpdateResource(netscaler.Cspolicy.Type(), cspolicyName, &cspolicy)
		if err != nil {
			return fmt.Errorf("Error updating cspolicy %s", cspolicyName)
		}
		log.Printf("[DEBUG] cspolicy has been updated  cspolicy %s ", cspolicyName)
	}

	if lbvserverChanged {
		//Binding has to be updated
		//rebind
		targetlbvserver, lbok := d.GetOk("targetlbvserver")
		priority, pok := d.GetOk("priority")

		if !pok && lbok {
			return fmt.Errorf("Need to specify priority if lbvserver is specified")
		}

		binding := cs.Csvservercspolicybinding{
			Name:            csvserver,
			Policyname:      cspolicyName,
			Targetlbvserver: targetlbvserver.(string),
			Priority:        priority.(int),
		}
		err := client.BindResource(netscaler.Csvserver.Type(), csvserver, netscaler.Cspolicy.Type(), cspolicyName, &binding)
		if err != nil {
			log.Printf("Failed to bind new cspolicy to Csvserver")
			return err
		}
		log.Printf("[DEBUG] cspolicy has been bound to csvserver  cspolicy %s csvserver %s", cspolicyName, csvserver)
	}

	return readCspolicyFunc(d, meta)
}

func deleteCspolicyFunc(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*NetScalerNitroClient).client
	cspolicyName := d.Id()
	csvserver := d.Get("csvserver").(string)

	//First we unbind from cs vserver if necessary
	err := client.UnbindResource(netscaler.Csvserver.Type(), csvserver, netscaler.Cspolicy.Type(), cspolicyName, "policyname")
	if err != nil {
		return fmt.Errorf("Error unbinding cspolicy from csvserver %s", cspolicyName)
	}
	err = client.DeleteResource(netscaler.Cspolicy.Type(), cspolicyName)
	if err != nil {
		return fmt.Errorf("Error  deleting cspolicy %s", cspolicyName)
	}

	d.SetId("")

	return nil
}
<|endoftext|>