<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"github.com/streadway/amqp"
	"koding/tools/log"
	"koding/tools/sockjs"
	"koding/tools/utils"
	"math/rand"
	"net"
	"net/http"
	"strings"
	"time"
)

func main() {
	utils.Startup("broker", false)
	utils.RunStatusLogger()

	utils.AmqpAutoReconnect("broker", func(consumeConn, publishConn *amqp.Connection) {

		service := sockjs.NewService("http://localhost/sockjs.js", true, false, 10*time.Minute, 0, func(receiveChan <-chan interface{}, sendChan chan<- interface{}) {
			defer log.RecoverAndLog()

			socketId := fmt.Sprintf("%x", rand.Int63())
			clientQueue := "broker-client-" + socketId
			exchanges := make([]string, 0)

			utils.ChangeNumClients <- 1
			log.Debug("Client connected: " + socketId)
			defer func() {
				utils.ChangeNumClients <- -1
				log.Debug("Client disconnected: " + socketId)
			}()

			controlChannel := utils.CreateAmqpChannel(publishConn)
			defer func() { controlChannel.Close() }() // controlChannel is replaced on error

			body, err := json.Marshal(map[string]string{"socket_id": socketId})
			if err != nil {
				panic(err)
			}
			err = controlChannel.Publish("private-broker", "connected", false, false, amqp.Publishing{Body: body})
			if err != nil {
				panic(err)
			}

			defer func() {
				body, err = json.Marshal(map[string]interface{}{"socket_id": socketId, "exchanges": exchanges})
				if err != nil {
					panic(err)
				}
				err = controlChannel.Publish("private-broker", "disconnected", false, false, amqp.Publishing{Body: body})
				if err != nil {
					panic(err)
				}
				body, err = json.Marshal(map[string]interface{}{"socket_id": socketId})
				if err != nil {
					panic(err)
				}
				for _, exchange := range exchanges {
					err = controlChannel.Publish(exchange, "disconnected", false, false, amqp.Publishing{Body: body})
					if err != nil {
						panic(err)
					}
				}
			}()

			consumeChannel := utils.CreateAmqpChannel(consumeConn)
			defer consumeChannel.Close()
			consumerFinished := make(chan bool)
			defer close(consumerFinished)

			_, err = consumeChannel.QueueDeclare(clientQueue, false, true, false, false, nil)
			if err != nil {
				panic(err)
			}

			stream, err := consumeChannel.Consume(clientQueue, "", true, false, false, false, nil)
			if err != nil {
				panic(err)
			}

			go func() {
				defer log.RecoverAndLog()
				defer func() { consumerFinished <- true }()

				for message := range stream {
					func() {
						defer log.RecoverAndLog()

						body, err = json.Marshal(map[string]string{"event": message.RoutingKey, "exchange": message.Exchange, "payload": string(message.Body)})
						if err != nil {
							panic(err)
						}
						select {
						case sendChan <- string(body):
							// successful
						default:
							log.Warn("Dropped message for client " + socketId)
						}
					}()
				}
			}()

			for data := range receiveChan {
				func() {
					defer func() {
						err := recover()
						if err != nil {
							log.LogError(err)
							controlChannel.Close()
							controlChannel = utils.CreateAmqpChannel(publishConn)
						}
					}()

					var message map[string]string
					err := json.Unmarshal([]byte(data.(string)), &message)
					if err != nil {
						panic(err)
					}
					log.Debug(message)

					event := message["event"]
					exchange := message["exchange"]
					routingKey := message["routingKey"]
					if routingKey == "" {
						routingKey = "#"
					}

					switch event {
					case "client-bind":
						err = controlChannel.QueueBind(clientQueue, routingKey, exchange, false, nil)
						if err != nil {
							panic(err)
						}
						exchanges = append(exchanges, exchange)

						body, err = json.Marshal(map[string]string{"event": "broker:bind_succeeded", "exchange": exchange, "routingKey": routingKey})
						if err != nil {
							log.Debug("there was an error")
							panic(err)
						}
						log.Debug(body + " ")
						sendChan <- string(body)

					case "client-unbind":
						err = controlChannel.QueueUnbind(clientQueue, routingKey, exchange, nil)
						if err != nil {
							panic(err)
						}
						for i, e := range exchanges {
							if e == exchange {
								exchanges[i] = exchanges[len(exchanges)-1]
								exchanges = exchanges[:len(exchanges)-1]
								break
							}
						}

					case "client-presence":

					default:
						if strings.HasPrefix(event, "client-") && strings.HasPrefix(exchange, "secret-") {
							err := controlChannel.Publish(exchange, event, false, false, amqp.Publishing{Body: []byte(message["payload"])})
							if err != nil {
								panic(err)
							}
						} else if message["vhost"] != "" {
							// ignored
						} else {
							log.Warn(fmt.Sprintf("Invalid message: %v", message))
						}

					}
				}()
			}

			consumeChannel.Close()
			<-consumerFinished
		})
		defer service.Close()

		server := &http.Server{Handler: &sockjs.Mux{
			Services: map[string]*sockjs.Service{
				"/subscribe": service,
			},
		}}
		listener, err := net.Listen("tcp", ":8008")
		if err != nil {
			panic(err)
		}

		go func() {
			for _ = range consumeConn.NotifyClose(make(chan *amqp.Error)) {
				listener.Close()
			}
		}()

		go func() {
			for _ = range publishConn.NotifyClose(make(chan *amqp.Error)) {
				listener.Close()
			}
		}()

		err = server.Serve(listener)
		if err != nil {
			log.Warn("Server error: " + err.Error())
		}
	})
}
<commit_msg>Fixed compile error.<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"github.com/streadway/amqp"
	"koding/tools/log"
	"koding/tools/sockjs"
	"koding/tools/utils"
	"math/rand"
	"net"
	"net/http"
	"strings"
	"time"
)

func main() {
	utils.Startup("broker", false)
	utils.RunStatusLogger()

	utils.AmqpAutoReconnect("broker", func(consumeConn, publishConn *amqp.Connection) {

		service := sockjs.NewService("http://localhost/sockjs.js", true, false, 10*time.Minute, 0, func(receiveChan <-chan interface{}, sendChan chan<- interface{}) {
			defer log.RecoverAndLog()

			socketId := fmt.Sprintf("%x", rand.Int63())
			clientQueue := "broker-client-" + socketId
			exchanges := make([]string, 0)

			utils.ChangeNumClients <- 1
			log.Debug("Client connected: " + socketId)
			defer func() {
				utils.ChangeNumClients <- -1
				log.Debug("Client disconnected: " + socketId)
			}()

			controlChannel := utils.CreateAmqpChannel(publishConn)
			defer func() { controlChannel.Close() }() // controlChannel is replaced on error

			body, err := json.Marshal(map[string]string{"socket_id": socketId})
			if err != nil {
				panic(err)
			}
			err = controlChannel.Publish("private-broker", "connected", false, false, amqp.Publishing{Body: body})
			if err != nil {
				panic(err)
			}

			defer func() {
				body, err = json.Marshal(map[string]interface{}{"socket_id": socketId, "exchanges": exchanges})
				if err != nil {
					panic(err)
				}
				err = controlChannel.Publish("private-broker", "disconnected", false, false, amqp.Publishing{Body: body})
				if err != nil {
					panic(err)
				}
				body, err = json.Marshal(map[string]interface{}{"socket_id": socketId})
				if err != nil {
					panic(err)
				}
				for _, exchange := range exchanges {
					err = controlChannel.Publish(exchange, "disconnected", false, false, amqp.Publishing{Body: body})
					if err != nil {
						panic(err)
					}
				}
			}()

			consumeChannel := utils.CreateAmqpChannel(consumeConn)
			defer consumeChannel.Close()
			consumerFinished := make(chan bool)
			defer close(consumerFinished)

			_, err = consumeChannel.QueueDeclare(clientQueue, false, true, false, false, nil)
			if err != nil {
				panic(err)
			}

			stream, err := consumeChannel.Consume(clientQueue, "", true, false, false, false, nil)
			if err != nil {
				panic(err)
			}

			go func() {
				defer log.RecoverAndLog()
				defer func() { consumerFinished <- true }()

				for message := range stream {
					func() {
						defer log.RecoverAndLog()

						body, err = json.Marshal(map[string]string{"event": message.RoutingKey, "exchange": message.Exchange, "payload": string(message.Body)})
						if err != nil {
							panic(err)
						}
						select {
						case sendChan <- string(body):
							// successful
						default:
							log.Warn("Dropped message for client " + socketId)
						}
					}()
				}
			}()

			for data := range receiveChan {
				func() {
					defer func() {
						err := recover()
						if err != nil {
							log.LogError(err)
							controlChannel.Close()
							controlChannel = utils.CreateAmqpChannel(publishConn)
						}
					}()

					var message map[string]string
					err := json.Unmarshal([]byte(data.(string)), &message)
					if err != nil {
						panic(err)
					}
					log.Debug(message)

					event := message["event"]
					exchange := message["exchange"]
					routingKey := message["routingKey"]
					if routingKey == "" {
						routingKey = "#"
					}

					switch event {
					case "client-bind":
						err = controlChannel.QueueBind(clientQueue, routingKey, exchange, false, nil)
						if err != nil {
							panic(err)
						}
						exchanges = append(exchanges, exchange)

						body, err = json.Marshal(map[string]string{"event": "broker:bind_succeeded", "exchange": exchange, "routingKey": routingKey})
						if err != nil {
							panic(err)
						}
						sendChan <- string(body)

					case "client-unbind":
						err = controlChannel.QueueUnbind(clientQueue, routingKey, exchange, nil)
						if err != nil {
							panic(err)
						}
						for i, e := range exchanges {
							if e == exchange {
								exchanges[i] = exchanges[len(exchanges)-1]
								exchanges = exchanges[:len(exchanges)-1]
								break
							}
						}

					case "client-presence":

					default:
						if strings.HasPrefix(event, "client-") && strings.HasPrefix(exchange, "secret-") {
							err := controlChannel.Publish(exchange, event, false, false, amqp.Publishing{Body: []byte(message["payload"])})
							if err != nil {
								panic(err)
							}
						} else if message["vhost"] != "" {
							// ignored
						} else {
							log.Warn(fmt.Sprintf("Invalid message: %v", message))
						}

					}
				}()
			}

			consumeChannel.Close()
			<-consumerFinished
		})
		defer service.Close()

		server := &http.Server{Handler: &sockjs.Mux{
			Services: map[string]*sockjs.Service{
				"/subscribe": service,
			},
		}}
		listener, err := net.Listen("tcp", ":8008")
		if err != nil {
			panic(err)
		}

		go func() {
			for _ = range consumeConn.NotifyClose(make(chan *amqp.Error)) {
				listener.Close()
			}
		}()

		go func() {
			for _ = range publishConn.NotifyClose(make(chan *amqp.Error)) {
				listener.Close()
			}
		}()

		err = server.Serve(listener)
		if err != nil {
			log.Warn("Server error: " + err.Error())
		}
	})
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/rand"
	"encoding/base64"
	"fmt"
	"koding/broker/storage"
	"koding/tools/sockjs"
	"strconv"
	"strings"
	"time"

	"github.com/fatih/set"
	"github.com/streadway/amqp"
)

type Client struct {
	// Holds SockJS session
	Session *sockjs.Session

	// ControlChannel for communicating with authworker
	ControlChannel *amqp.Channel

	// Holds the socket id for Client Session
	SocketId string

	// Main broker singleton
	Broker *Broker

	// LastPayload is used for trying to send the same payload again
	// if any error occures while publishing
	LastPayload string

	// Subscriptions holds subscriptions of the current client
	Subscriptions storage.Subscriptionable
}

// NewClient retuns a new client which represents the connected client
// it holds required information about the client/session
func NewClient(session *sockjs.Session, broker *Broker) (*Client, error) {
	socketId := randomString()
	session.Tag = socketId

	controlChannel, err := broker.PublishConn.Channel()
	if err != nil {
		return nil, fmt.Errorf("Couldnt create publish channel %v", err)
	}

	subscriptions, err := createSubscriptionStorage(socketId)
	if err != nil {
		return nil, err
	}

	globalMapMutex.Lock()
	sessionsMap[socketId] = session
	globalMapMutex.Unlock()

	return &Client{
		Session:        session,
		SocketId:       socketId,
		ControlChannel: controlChannel,
		Broker:         broker,
		Subscriptions:  subscriptions,
	}, nil
}

// createSubscriptionStorage arranges a storage place for subscriptions
// it can be Redis backend or inmemory Set storage
func createSubscriptionStorage(socketId string) (storage.Subscriptionable, error) {

	// first try to create a redis storage
	if subscriptions, err := storage.NewStorage(conf, storage.REDIS, socketId); err == nil {
		// if we success, just return the storage
		return subscriptions, nil
	} else {
		log.Critical("Couldnt access to redis/create a key for client %v: Error: %v", socketId, err)
	}
	// if we try to create subscription storage backend with redis and fail
	// create an inmemory storage system
	if subscriptions, err := storage.NewStorage(conf, storage.SET, socketId); err == nil {
		return subscriptions, nil
	}

	// this will never fail to here
	return nil, fmt.Errorf("Couldnt create subscription storage for Client: %v", socketId)
}

// Close should be called whenever a client disconnects.
// Close removes client's subscriptions from routeMap immediately
// It waits for 5 minutes before clearing the client's subscriptions because if this
// is a temp glicth on network client should be able to resubscribe to all of them again
func (c *Client) Close() {
	log.Debug("Client Close Request for socketID: %v", c.SocketId)
	c.Subscriptions.Each(func(routingKeyPrefix interface{}) bool {
		c.RemoveFromRoute(routingKeyPrefix.(string))
		return true
	})

	c.Subscriptions.ClearWithTimeout(time.Minute * 5)

	for {
		err := c.ControlChannel.Publish(c.Broker.Config.AuthAllExchange, "broker.clientDisconnected", false, false, amqp.Publishing{Body: []byte(c.SocketId)})
		if err == nil {
			break
		}
		if amqpError, isAmqpError := err.(*amqp.Error); !isAmqpError || amqpError.Code != amqp.ChannelError {
			log.Critical("Error while publising -not rabbitmq error- %v", err)
		}
		c.resetControlChannel()
	}

	log.Debug("Closing control channel for socketID: %v", c.SocketId)
	c.ControlChannel.Close()

	globalMapMutex.Lock()
	defer globalMapMutex.Unlock()
	delete(sessionsMap, c.SocketId)
}

// handleSessionMessage handles the received message from the client. It
// passes a response back to the client or publish the received message to a
// rabbitmq exchange for further process.
func (c *Client) handleSessionMessage(data interface{}) {
	message := data.(map[string]interface{})
	log.Debug("Received message: %v", message)

	action := message["action"]
	switch action {
	case "subscribe":
		routingKeyPrefixes := strings.Split(message["routingKeyPrefix"].(string), " ")
		if err := c.Subscribe(routingKeyPrefixes...); err != nil {
			log.Error(err.Error())
		}

		sendToClient(c.Session, "broker.subscribed", message["routingKeyPrefix"])

	case "resubscribe":
		clientId := message["socketId"].(string)
		log.Debug("Resubscribe event for clientId: %v SocketId: %v", clientId, c.SocketId)
		found, err := c.Resubscribe(clientId)
		if err != nil {
			log.Error(err.Error())
		}
		log.Debug("Resubscribe found for socketID: %v, %v", clientId, found)
		sendToClient(c.Session, "broker.resubscribed", found)

	case "unsubscribe":
		routingKeyPrefix := message["routingKeyPrefix"].(string)
		log.Debug("Unsubscribe event for socketID: %v, and prefixes", c.SocketId, routingKeyPrefix)
		routingKeyPrefixes := strings.Split(routingKeyPrefix, " ")
		c.Unsubscribe(routingKeyPrefixes...)

	case "publish":
		exchange := message["exchange"].(string)
		routingKey := message["routingKey"].(string)
		payload := message["payload"].(string)

		log.Debug("Publish Event: Exchange: %v, RoutingKey %v, Payload %v",
			exchange,
			routingKey,
			payload,
		)

		if err := c.Publish(exchange, routingKey, payload); err != nil {
			log.Error(err.Error())
		}

	case "ping":
		sendToClient(c.Session, "broker.pong", nil)
		if c.Subscriptions.Backend() == storage.REDIS {
			// TOOD - may be we need to revisit this part later about duration and request count
			go c.Subscriptions.ClearWithTimeout(time.Minute * 59)
		}
	default:
		log.Warning("Invalid action. message: %v socketId: %v", message, c.SocketId)

	}
}

// Publish publish the given payload for to the given exchange and routingkey.
// if publishing fails for given payload waits for quarter of a second
func (c *Client) Publish(exchange, routingKey, payload string) error {
	if !strings.HasPrefix(routingKey, "client.") {
		return fmt.Errorf("Invalid routing key: %v socketId: %v", routingKey, c.SocketId)
	}

	for {
		c.LastPayload = ""
		err := c.ControlChannel.Publish(exchange, routingKey, false, false, amqp.Publishing{CorrelationId: c.SocketId, Body: []byte(payload)})
		if err == nil {
			c.LastPayload = payload
			break
		}

		time.Sleep(time.Second / 4) // penalty for crashing the AMQP channel
		c.resetControlChannel()
	}

	return nil
}

// gaugeStart starts the gauge for a given session. It returns a new
// function which ends the gauge for the given session. Usually one invokes
// gaugeStart and calls the returned function in a defer statement.
func (c *Client) gaugeStart() (gaugeEnd func()) {
	log.Debug("Client connected: %v", c.Session.Tag)
	changeClientsGauge(1)
	changeNewClientsGauge(1)
	if c.Session.IsWebsocket {
		changeWebsocketClientsGauge(1)
	}

	return func() {
		log.Debug("Client disconnected: %v", c.Session.Tag)
		changeClientsGauge(-1)
		if c.Session.IsWebsocket {
			changeWebsocketClientsGauge(-1)
		}
	}
}

// resetControlChannel closes the current client's control channel and creates
// a new channel. It also listens to any server side error and publish back
// the error to the client.
func (c *Client) resetControlChannel() {
	defer log.RecoverAndLog()

	if c.ControlChannel != nil {
		c.ControlChannel.Close()
	}

	var err error
	c.ControlChannel, err = c.Broker.PublishConn.Channel()
	if err != nil {
		log.Critical("Couldnt create publishing channel %v", err)
	}

	go func() {
		defer log.RecoverAndLog()

		for amqpErr := range c.ControlChannel.NotifyClose(make(chan *amqp.Error)) {
			if !(strings.Contains(amqpErr.Error(), "NOT_FOUND") && (strings.Contains(amqpErr.Error(), "koding-social-") || strings.Contains(amqpErr.Error(), "auth-"))) {
				log.Warning("AMQP channel: %v Last publish payload: %v", amqpErr.Error(), c.LastPayload)
			}

			sendToClient(c.Session, "broker.error", map[string]interface{}{
				"code":    amqpErr.Code,
				"reason":  amqpErr.Reason,
				"server":  amqpErr.Server,
				"recover": amqpErr.Recover,
			})
		}
	}()
}

// RemoveFromRoute removes the sessions for the given routingKeyPrefix.
func (c *Client) RemoveFromRoute(routingKeyPrefixes ...string) {
	for _, routingKeyPrefix := range routingKeyPrefixes {
		if _, ok := routeMap[routingKeyPrefix]; !ok {
			continue
		}
		routeMap[routingKeyPrefix].Remove(c.SocketId)

		if routeMap[routingKeyPrefix].Size() == 0 {
			delete(routeMap, routingKeyPrefix)
		}
	}
}

// AddToRoute ads routes to the routeMap for client
func (c *Client) AddToRoute() {
	c.Subscriptions.Each(func(routingKeyPrefix interface{}) bool {
		c.AddToRouteMapNOTS(routingKeyPrefix.(string))
		return true
	})
}

// AddToRouteMapNOTS adds given routingKeys to the global routemap
// it is non-thread-safe function, developers should use it with their
// own thread safe wrapping
func (c *Client) AddToRouteMapNOTS(routingKeyPrefixes ...string) {
	for _, routingKeyPrefix := range routingKeyPrefixes {
		if _, ok := routeMap[routingKeyPrefix]; !ok {
			routeMap[routingKeyPrefix] = set.New()
		}
		routeMap[routingKeyPrefix].Add(c.SocketId)
	}
}

// Subscribe add the given routingKeyPrefix to the list of subscriptions
// associated with this client.
func (c *Client) Subscribe(routingKeyPrefixes ...string) error {
	if err := c.Subscriptions.Subscribe(routingKeyPrefixes...); err != nil {
		return err
	}

	globalMapMutex.Lock()
	c.AddToRouteMapNOTS(routingKeyPrefixes...)
	globalMapMutex.Unlock()

	// Log some information about the Client
	go func() {
		length, err := c.Subscriptions.Len()
		if err != nil {
			log.Warning("Error while trying to get Subscriptions.Len() for: %v Error: %v", c.Session.Tag, err)
		}

		if length > 0 && length%2000 == 0 {
			log.Warning("Client with more than %v subscriptions %v", strconv.Itoa(length), c.Session.Tag)
		}
	}()

	return nil
}

// Resubscribe tries to resubscribe with another sessionId
// it is useful when client disconnected and a while after
// tries to subscribe again, so there will not be that many
// communication between broker and the client
func (c *Client) Resubscribe(sessionId string) (bool, error) {
	found, err := c.Subscriptions.Resubscribe(sessionId)
	if err != nil {
		return false, err
	}

	if !found {
		return false, nil
	}

	c.AddToRoute()
	return true, nil
}

// Unsubscribe deletes the given routingKey prefix from the subscription list
// and removes it from the global route map
func (c *Client) Unsubscribe(routingKeyPrefixes ...string) {
	c.RemoveFromRoute(routingKeyPrefixes...)
	if err := c.Subscriptions.Unsubscribe(routingKeyPrefixes...); err != nil {
		fmt.Errorf("%v", err)
	}
}

// randomString() returns a new 16 char length random string
func randomString() string {
	r := make([]byte, 128/8)
	rand.Read(r)
	return base64.StdEncoding.EncodeToString(r)
}
<commit_msg>Broker: fix typo<commit_after>package main

import (
	"crypto/rand"
	"encoding/base64"
	"fmt"
	"koding/broker/storage"
	"koding/tools/sockjs"
	"strconv"
	"strings"
	"time"

	"github.com/fatih/set"
	"github.com/streadway/amqp"
)

type Client struct {
	// Holds SockJS session
	Session *sockjs.Session

	// ControlChannel for communicating with authworker
	ControlChannel *amqp.Channel

	// Holds the socket id for Client Session
	SocketId string

	// Main broker singleton
	Broker *Broker

	// LastPayload is used for trying to send the same payload again
	// if any error occures while publishing
	LastPayload string

	// Subscriptions holds subscriptions of the current client
	Subscriptions storage.Subscriptionable
}

// NewClient retuns a new client which represents the connected client
// it holds required information about the client/session
func NewClient(session *sockjs.Session, broker *Broker) (*Client, error) {
	socketId := randomString()
	session.Tag = socketId

	controlChannel, err := broker.PublishConn.Channel()
	if err != nil {
		return nil, fmt.Errorf("Couldnt create publish channel %v", err)
	}

	subscriptions, err := createSubscriptionStorage(socketId)
	if err != nil {
		return nil, err
	}

	globalMapMutex.Lock()
	sessionsMap[socketId] = session
	globalMapMutex.Unlock()

	return &Client{
		Session:        session,
		SocketId:       socketId,
		ControlChannel: controlChannel,
		Broker:         broker,
		Subscriptions:  subscriptions,
	}, nil
}

// createSubscriptionStorage arranges a storage place for subscriptions
// it can be Redis backend or inmemory Set storage
func createSubscriptionStorage(socketId string) (storage.Subscriptionable, error) {

	// first try to create a redis storage
	if subscriptions, err := storage.NewStorage(conf, storage.REDIS, socketId); err == nil {
		// if we success, just return the storage
		return subscriptions, nil
	} else {
		log.Critical("Couldnt access to redis/create a key for client %v: Error: %v", socketId, err)
	}
	// if we try to create subscription storage backend with redis and fail
	// create an inmemory storage system
	if subscriptions, err := storage.NewStorage(conf, storage.SET, socketId); err == nil {
		return subscriptions, nil
	}

	// this will never fail to here
	return nil, fmt.Errorf("Couldnt create subscription storage for Client: %v", socketId)
}

// Close should be called whenever a client disconnects.
// Close removes client's subscriptions from routeMap immediately
// It waits for 5 minutes before clearing the client's subscriptions because if this
// is a temp glitch on network client should be able to resubscribe to all of them again
func (c *Client) Close() {
	log.Debug("Client Close Request for socketID: %v", c.SocketId)
	c.Subscriptions.Each(func(routingKeyPrefix interface{}) bool {
		c.RemoveFromRoute(routingKeyPrefix.(string))
		return true
	})

	c.Subscriptions.ClearWithTimeout(time.Minute * 5)

	for {
		err := c.ControlChannel.Publish(c.Broker.Config.AuthAllExchange, "broker.clientDisconnected", false, false, amqp.Publishing{Body: []byte(c.SocketId)})
		if err == nil {
			break
		}
		if amqpError, isAmqpError := err.(*amqp.Error); !isAmqpError || amqpError.Code != amqp.ChannelError {
			log.Critical("Error while publising -not rabbitmq error- %v", err)
		}
		c.resetControlChannel()
	}

	log.Debug("Closing control channel for socketID: %v", c.SocketId)
	c.ControlChannel.Close()

	globalMapMutex.Lock()
	defer globalMapMutex.Unlock()
	delete(sessionsMap, c.SocketId)
}

// handleSessionMessage handles the received message from the client. It
// passes a response back to the client or publish the received message to a
// rabbitmq exchange for further process.
func (c *Client) handleSessionMessage(data interface{}) {
	message := data.(map[string]interface{})
	log.Debug("Received message: %v", message)

	action := message["action"]
	switch action {
	case "subscribe":
		routingKeyPrefixes := strings.Split(message["routingKeyPrefix"].(string), " ")
		if err := c.Subscribe(routingKeyPrefixes...); err != nil {
			log.Error(err.Error())
		}

		sendToClient(c.Session, "broker.subscribed", message["routingKeyPrefix"])

	case "resubscribe":
		clientId := message["socketId"].(string)
		log.Debug("Resubscribe event for clientId: %v SocketId: %v", clientId, c.SocketId)
		found, err := c.Resubscribe(clientId)
		if err != nil {
			log.Error(err.Error())
		}
		log.Debug("Resubscribe found for socketID: %v, %v", clientId, found)
		sendToClient(c.Session, "broker.resubscribed", found)

	case "unsubscribe":
		routingKeyPrefix := message["routingKeyPrefix"].(string)
		log.Debug("Unsubscribe event for socketID: %v, and prefixes", c.SocketId, routingKeyPrefix)
		routingKeyPrefixes := strings.Split(routingKeyPrefix, " ")
		c.Unsubscribe(routingKeyPrefixes...)

	case "publish":
		exchange := message["exchange"].(string)
		routingKey := message["routingKey"].(string)
		payload := message["payload"].(string)

		log.Debug("Publish Event: Exchange: %v, RoutingKey %v, Payload %v",
			exchange,
			routingKey,
			payload,
		)

		if err := c.Publish(exchange, routingKey, payload); err != nil {
			log.Error(err.Error())
		}

	case "ping":
		sendToClient(c.Session, "broker.pong", nil)
		if c.Subscriptions.Backend() == storage.REDIS {
			// TOOD - may be we need to revisit this part later about duration and request count
			go c.Subscriptions.ClearWithTimeout(time.Minute * 59)
		}
	default:
		log.Warning("Invalid action. message: %v socketId: %v", message, c.SocketId)

	}
}

// Publish publish the given payload for to the given exchange and routingkey.
// if publishing fails for given payload waits for quarter of a second
func (c *Client) Publish(exchange, routingKey, payload string) error {
	if !strings.HasPrefix(routingKey, "client.") {
		return fmt.Errorf("Invalid routing key: %v socketId: %v", routingKey, c.SocketId)
	}

	for {
		c.LastPayload = ""
		err := c.ControlChannel.Publish(exchange, routingKey, false, false, amqp.Publishing{CorrelationId: c.SocketId, Body: []byte(payload)})
		if err == nil {
			c.LastPayload = payload
			break
		}

		time.Sleep(time.Second / 4) // penalty for crashing the AMQP channel
		c.resetControlChannel()
	}

	return nil
}

// gaugeStart starts the gauge for a given session. It returns a new
// function which ends the gauge for the given session. Usually one invokes
// gaugeStart and calls the returned function in a defer statement.
func (c *Client) gaugeStart() (gaugeEnd func()) {
	log.Debug("Client connected: %v", c.Session.Tag)
	changeClientsGauge(1)
	changeNewClientsGauge(1)
	if c.Session.IsWebsocket {
		changeWebsocketClientsGauge(1)
	}

	return func() {
		log.Debug("Client disconnected: %v", c.Session.Tag)
		changeClientsGauge(-1)
		if c.Session.IsWebsocket {
			changeWebsocketClientsGauge(-1)
		}
	}
}

// resetControlChannel closes the current client's control channel and creates
// a new channel. It also listens to any server side error and publish back
// the error to the client.
func (c *Client) resetControlChannel() {
	defer log.RecoverAndLog()

	if c.ControlChannel != nil {
		c.ControlChannel.Close()
	}

	var err error
	c.ControlChannel, err = c.Broker.PublishConn.Channel()
	if err != nil {
		log.Critical("Couldnt create publishing channel %v", err)
	}

	go func() {
		defer log.RecoverAndLog()

		for amqpErr := range c.ControlChannel.NotifyClose(make(chan *amqp.Error)) {
			if !(strings.Contains(amqpErr.Error(), "NOT_FOUND") && (strings.Contains(amqpErr.Error(), "koding-social-") || strings.Contains(amqpErr.Error(), "auth-"))) {
				log.Warning("AMQP channel: %v Last publish payload: %v", amqpErr.Error(), c.LastPayload)
			}

			sendToClient(c.Session, "broker.error", map[string]interface{}{
				"code":    amqpErr.Code,
				"reason":  amqpErr.Reason,
				"server":  amqpErr.Server,
				"recover": amqpErr.Recover,
			})
		}
	}()
}

// RemoveFromRoute removes the sessions for the given routingKeyPrefix.
func (c *Client) RemoveFromRoute(routingKeyPrefixes ...string) {
	for _, routingKeyPrefix := range routingKeyPrefixes {
		if _, ok := routeMap[routingKeyPrefix]; !ok {
			continue
		}
		routeMap[routingKeyPrefix].Remove(c.SocketId)

		if routeMap[routingKeyPrefix].Size() == 0 {
			delete(routeMap, routingKeyPrefix)
		}
	}
}

// AddToRoute ads routes to the routeMap for client
func (c *Client) AddToRoute() {
	c.Subscriptions.Each(func(routingKeyPrefix interface{}) bool {
		c.AddToRouteMapNOTS(routingKeyPrefix.(string))
		return true
	})
}

// AddToRouteMapNOTS adds given routingKeys to the global routemap
// it is non-thread-safe function, developers should use it with their
// own thread safe wrapping
func (c *Client) AddToRouteMapNOTS(routingKeyPrefixes ...string) {
	for _, routingKeyPrefix := range routingKeyPrefixes {
		if _, ok := routeMap[routingKeyPrefix]; !ok {
			routeMap[routingKeyPrefix] = set.New()
		}
		routeMap[routingKeyPrefix].Add(c.SocketId)
	}
}

// Subscribe add the given routingKeyPrefix to the list of subscriptions
// associated with this client.
func (c *Client) Subscribe(routingKeyPrefixes ...string) error {
	if err := c.Subscriptions.Subscribe(routingKeyPrefixes...); err != nil {
		return err
	}

	globalMapMutex.Lock()
	c.AddToRouteMapNOTS(routingKeyPrefixes...)
	globalMapMutex.Unlock()

	// Log some information about the Client
	go func() {
		length, err := c.Subscriptions.Len()
		if err != nil {
			log.Warning("Error while trying to get Subscriptions.Len() for: %v Error: %v", c.Session.Tag, err)
		}

		if length > 0 && length%2000 == 0 {
			log.Warning("Client with more than %v subscriptions %v", strconv.Itoa(length), c.Session.Tag)
		}
	}()

	return nil
}

// Resubscribe tries to resubscribe with another sessionId
// it is useful when client disconnected and a while after
// tries to subscribe again, so there will not be that many
// communication between broker and the client
func (c *Client) Resubscribe(sessionId string) (bool, error) {
	found, err := c.Subscriptions.Resubscribe(sessionId)
	if err != nil {
		return false, err
	}

	if !found {
		return false, nil
	}

	c.AddToRoute()
	return true, nil
}

// Unsubscribe deletes the given routingKey prefix from the subscription list
// and removes it from the global route map
func (c *Client) Unsubscribe(routingKeyPrefixes ...string) {
	c.RemoveFromRoute(routingKeyPrefixes...)
	if err := c.Subscriptions.Unsubscribe(routingKeyPrefixes...); err != nil {
		fmt.Errorf("%v", err)
	}
}

// randomString() returns a new 16 char length random string
func randomString() string {
	r := make([]byte, 128/8)
	rand.Read(r)
	return base64.StdEncoding.EncodeToString(r)
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"fmt"
	forecast "github.com/mlbright/darksky/v2"
	"io/ioutil"
	"log"
	"os"
	"regexp"
	"strings"
)

type gps struct {
	name string
	lat  string
	long string
}

var key string

func Scities(cities []gps) string {
	res := []string{}
	for _, city := range cities {
		f, err := forecast.Get(key, city.lat, city.long, "now", forecast.CA)
		if err != nil {
			log.Fatal(err)
		}
		res = append(res, fmt.Sprintf("%s %s %dC (%dC) H:%d W:%dkm/h", city.name, f.Currently.Summary, Round(f.Currently.Temperature), Round(f.Currently.ApparentTemperature), Round(f.Currently.Humidity*100), Round(f.Currently.WindSpeed)))
	}
	return strings.Join(res, " | ")
}

func Round(value float64) int {
	if value < 0.0 {
		value -= 0.5
	} else {
		value += 0.5
	}
	return int(value)
}

func main() {
	keybytes, err := ioutil.ReadFile("darksky_key.txt")
	if err != nil {
		log.Fatal(err)
	}
	key = string(keybytes)
	key = strings.TrimSpace(key)

	cities := []gps{
		{"Amsterdam", "52.3745", "4.898"},
		{"Budapest", "47.4984", "19.0405"},
		{"Cournonsec", "43.5482", "3.7"},
		{"Dijon", "47.3216", "5.0415"},
		{"Marseille", "43.2962", "5.37"},
		{"Masquière", "43.4602", "1.578"},
		{"Montréal", "45.5088", "-73.554"},
	}

	bio := bufio.NewReader(os.Stdin)
	r, _ := regexp.Compile("PRIVMSG (#\\S+) ::meteo")
	for {
		line, err := bio.ReadString('\n')
		if err != nil {
			fmt.Println(err)
		} else {
			par := r.FindStringSubmatch(line)
			if par != nil {
				fmt.Printf("PRIVMSG %s :%s\n", par[1], Scities(cities))
			}
		}
	}
}
<commit_msg>Update language to french (#2)<commit_after>package main

import (
	"bufio"
	"fmt"
	forecast "github.com/mlbright/darksky/v2"
	"io/ioutil"
	"log"
	"os"
	"regexp"
	"strings"
)

type gps struct {
	name string
	lat  string
	long string
}

var key string

func Scities(cities []gps) string {
	res := []string{}
	for _, city := range cities {
		f, err := forecast.Get(key, city.lat, city.long, "now", forecast.CA, forecast.French)
		if err != nil {
			log.Fatal(err)
		}
		res = append(res, fmt.Sprintf("%s %s %dC (%dC) H:%d W:%dkm/h", city.name, f.Currently.Summary, Round(f.Currently.Temperature), Round(f.Currently.ApparentTemperature), Round(f.Currently.Humidity*100), Round(f.Currently.WindSpeed)))
	}
	return strings.Join(res, " | ")
}

func Round(value float64) int {
	if value < 0.0 {
		value -= 0.5
	} else {
		value += 0.5
	}
	return int(value)
}

func main() {
	keybytes, err := ioutil.ReadFile("darksky_key.txt")
	if err != nil {
		log.Fatal(err)
	}
	key = string(keybytes)
	key = strings.TrimSpace(key)

	cities := []gps{
		{"Amsterdam", "52.3745", "4.898"},
		{"Budapest", "47.4984", "19.0405"},
		{"Cournonsec", "43.5482", "3.7"},
		{"Dijon", "47.3216", "5.0415"},
		{"Marseille", "43.2962", "5.37"},
		{"Masquière", "43.4602", "1.578"},
		{"Montréal", "45.5088", "-73.554"},
	}

	bio := bufio.NewReader(os.Stdin)
	r, _ := regexp.Compile("PRIVMSG (#\\S+) ::meteo")
	for {
		line, err := bio.ReadString('\n')
		if err != nil {
			fmt.Println(err)
		} else {
			par := r.FindStringSubmatch(line)
			if par != nil {
				fmt.Printf("PRIVMSG %s :%s\n", par[1], Scities(cities))
			}
		}
	}
}
<|endoftext|>


<commit_before>// Copyright ©2016 The Gonum 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 community

import (
	"fmt"
	"log"
	"sort"
	"testing"

	"gonum.org/v1/gonum/graph"
	"gonum.org/v1/gonum/graph/internal/ordered"
	"gonum.org/v1/gonum/graph/simple"
)

func ExampleProfile_simple() {
	// Create dumbell graph:
	//
	//  0       4
	//  |\     /|
	//  | 2 - 3 |
	//  |/     \|
	//  1       5
	//
	g := simple.NewUndirectedGraph()
	for u, e := range smallDumbell {
		for v := range e {
			g.SetEdge(simple.Edge{F: simple.Node(u), T: simple.Node(v)})
		}
	}

	// Get the profile of internal node weight for resolutions
	// between 0.1 and 10 using logarithmic bisection.
	p, err := Profile(ModularScore(g, Weight, 10, nil), true, 1e-3, 0.1, 10)
	if err != nil {
		log.Fatal(err)
	}

	// Print out each step with communities ordered.
	for _, d := range p {
		comm := d.Communities()
		for _, c := range comm {
			sort.Sort(ordered.ByID(c))
		}
		sort.Sort(ordered.BySliceIDs(comm))
		fmt.Printf("Low:%.2v High:%.2v Score:%v Communities:%v Q=%.3v\n",
			d.Low, d.High, d.Score, comm, Q(g, comm, d.Low))
	}

	// Output:
	// Low:0.1 High:0.29 Score:14 Communities:[[0 1 2 3 4 5]] Q=0.9
	// Low:0.29 High:2.3 Score:12 Communities:[[0 1 2] [3 4 5]] Q=0.714
	// Low:2.3 High:3.5 Score:4 Communities:[[0 1] [2] [3] [4 5]] Q=-0.31
	// Low:3.5 High:10 Score:0 Communities:[[0] [1] [2] [3] [4] [5]] Q=-0.607
}

var friends, enemies *simple.WeightedUndirectedGraph

func init() {
	friends = simple.NewWeightedUndirectedGraph(0, 0)
	for u, e := range middleEast.friends {
		// Ensure unconnected nodes are included.
		if friends.Node(int64(u)) == nil {
			friends.AddNode(simple.Node(u))
		}
		for v := range e {
			friends.SetWeightedEdge(simple.WeightedEdge{F: simple.Node(u), T: simple.Node(v), W: 1})
		}
	}
	enemies = simple.NewWeightedUndirectedGraph(0, 0)
	for u, e := range middleEast.enemies {
		// Ensure unconnected nodes are included.
		if enemies.Node(int64(u)) == nil {
			enemies.AddNode(simple.Node(u))
		}
		for v := range e {
			enemies.SetWeightedEdge(simple.WeightedEdge{F: simple.Node(u), T: simple.Node(v), W: -1})
		}
	}
}

func ExampleProfile_multiplex() {
	// The undirected graphs, friends and enemies, are the political relationships
	// in the Middle East as described in the Slate article:
	// http://www.slate.com/blogs/the_world_/2014/07/17/the_middle_east_friendship_chart.html
	g, err := NewUndirectedLayers(friends, enemies)
	if err != nil {
		log.Fatal(err)
	}
	weights := []float64{1, -1}

	// Get the profile of internal node weight for resolutions
	// between 0.1 and 10 using logarithmic bisection.
	p, err := Profile(ModularMultiplexScore(g, weights, true, WeightMultiplex, 10, nil), true, 1e-3, 0.1, 10)
	if err != nil {
		log.Fatal(err)
	}

	// Print out each step with communities ordered.
	for _, d := range p {
		comm := d.Communities()
		for _, c := range comm {
			sort.Sort(ordered.ByID(c))
		}
		sort.Sort(ordered.BySliceIDs(comm))
		fmt.Printf("Low:%.2v High:%.2v Score:%v Communities:%v Q=%.3v\n",
			d.Low, d.High, d.Score, comm, QMultiplex(g, comm, weights, []float64{d.Low}))
	}

	// Output:
	// Low:0.1 High:0.72 Score:26 Communities:[[0] [1 7 9 12] [2 8 11] [3 4 5 10] [6]] Q=[24.7 1.97]
	// Low:0.72 High:1.1 Score:24 Communities:[[0 6] [1 7 9 12] [2 8 11] [3 4 5 10]] Q=[16.9 14.1]
	// Low:1.1 High:1.2 Score:18 Communities:[[0 2 6 11] [1 7 9 12] [3 4 5 8 10]] Q=[9.16 25.1]
	// Low:1.2 High:1.6 Score:10 Communities:[[0 3 4 5 6 10] [1 7 9 12] [2 8 11]] Q=[10.5 26.5]
	// Low:1.6 High:1.6 Score:8 Communities:[[0 1 6 7 9 12] [2 8 11] [3 4 5 10]] Q=[5.56 39.8]
	// Low:1.6 High:1.8 Score:2 Communities:[[0 2 3 4 5 6 10] [1 7 8 9 11 12]] Q=[-1.82 48.6]
	// Low:1.8 High:2.3 Score:-6 Communities:[[0 2 3 4 5 6 8 10 11] [1 7 9 12]] Q=[-5 57.5]
	// Low:2.3 High:2.4 Score:-10 Communities:[[0 1 2 6 7 8 9 11 12] [3 4 5 10]] Q=[-11.2 79]
	// Low:2.4 High:4.3 Score:-52 Communities:[[0 1 2 3 4 5 6 7 8 9 10 11 12]] Q=[-46.1 117]
	// Low:4.3 High:10 Score:-54 Communities:[[0 1 2 3 4 6 7 8 9 10 11 12] [5]] Q=[-82 254]
}

func TestProfileUndirected(t *testing.T) {
	for _, test := range communityUndirectedQTests {
		g := simple.NewUndirectedGraph()
		for u, e := range test.g {
			// Add nodes that are not defined by an edge.
			if g.Node(int64(u)) == nil {
				g.AddNode(simple.Node(u))
			}
			for v := range e {
				g.SetEdge(simple.Edge{F: simple.Node(u), T: simple.Node(v)})
			}
		}

		testProfileUndirected(t, test, g)
	}
}

func TestProfileWeightedUndirected(t *testing.T) {
	for _, test := range communityUndirectedQTests {
		g := simple.NewWeightedUndirectedGraph(0, 0)
		for u, e := range test.g {
			// Add nodes that are not defined by an edge.
			if g.Node(int64(u)) == nil {
				g.AddNode(simple.Node(u))
			}
			for v := range e {
				g.SetWeightedEdge(simple.WeightedEdge{F: simple.Node(u), T: simple.Node(v), W: 1})
			}
		}

		testProfileUndirected(t, test, g)
	}
}

func testProfileUndirected(t *testing.T, test communityUndirectedQTest, g graph.Undirected) {
	fn := ModularScore(g, Weight, 10, nil)
	p, err := Profile(fn, true, 1e-3, 0.1, 10)
	if err != nil {
		t.Errorf("%s: unexpected error: %v", test.name, err)
	}

	const tries = 1000
	for i, d := range p {
		var score float64
		for i := 0; i < tries; i++ {
			score, _ = fn(d.Low)
			if score >= d.Score {
				break
			}
		}
		if score < d.Score {
			t.Errorf("%s: failed to recover low end score: got: %v want: %v", test.name, score, d.Score)
		}
		if i != 0 && d.Score >= p[i-1].Score {
			t.Errorf("%s: not monotonically decreasing: %v -> %v", test.name, p[i-1], d)
		}
	}
}

func TestProfileDirected(t *testing.T) {
	for _, test := range communityDirectedQTests {
		g := simple.NewDirectedGraph()
		for u, e := range test.g {
			// Add nodes that are not defined by an edge.
			if g.Node(int64(u)) == nil {
				g.AddNode(simple.Node(u))
			}
			for v := range e {
				g.SetEdge(simple.Edge{F: simple.Node(u), T: simple.Node(v)})
			}
		}

		testProfileDirected(t, test, g)
	}
}

func TestProfileWeightedDirected(t *testing.T) {
	for _, test := range communityDirectedQTests {
		g := simple.NewWeightedDirectedGraph(0, 0)
		for u, e := range test.g {
			// Add nodes that are not defined by an edge.
			if g.Node(int64(u)) == nil {
				g.AddNode(simple.Node(u))
			}
			for v := range e {
				g.SetWeightedEdge(simple.WeightedEdge{F: simple.Node(u), T: simple.Node(v), W: 1})
			}
		}

		testProfileDirected(t, test, g)
	}
}

func testProfileDirected(t *testing.T, test communityDirectedQTest, g graph.Directed) {
	fn := ModularScore(g, Weight, 10, nil)
	p, err := Profile(fn, true, 1e-3, 0.1, 10)
	if err != nil {
		t.Errorf("%s: unexpected error: %v", test.name, err)
	}

	const tries = 1000
	for i, d := range p {
		var score float64
		for i := 0; i < tries; i++ {
			score, _ = fn(d.Low)
			if score >= d.Score {
				break
			}
		}
		if score < d.Score {
			t.Errorf("%s: failed to recover low end score: got: %v want: %v", test.name, score, d.Score)
		}
		if i != 0 && d.Score >= p[i-1].Score {
			t.Errorf("%s: not monotonically decreasing: %v -> %v", test.name, p[i-1], d)
		}
	}
}

func TestProfileUndirectedMultiplex(t *testing.T) {
	for _, test := range communityUndirectedMultiplexQTests {
		g, weights, err := undirectedMultiplexFrom(test.layers)
		if err != nil {
			t.Errorf("unexpected error creating multiplex: %v", err)
			continue
		}

		const all = true

		fn := ModularMultiplexScore(g, weights, all, WeightMultiplex, 10, nil)
		p, err := Profile(fn, true, 1e-3, 0.1, 10)
		if err != nil {
			t.Errorf("%s: unexpected error: %v", test.name, err)
		}

		const tries = 1000
		for i, d := range p {
			var score float64
			for i := 0; i < tries; i++ {
				score, _ = fn(d.Low)
				if score >= d.Score {
					break
				}
			}
			if score < d.Score {
				t.Errorf("%s: failed to recover low end score: got: %v want: %v", test.name, score, d.Score)
			}
			if i != 0 && d.Score >= p[i-1].Score {
				t.Errorf("%s: not monotonically decreasing: %v -> %v", test.name, p[i-1], d)
			}
		}
	}
}

func TestProfileDirectedMultiplex(t *testing.T) {
	for _, test := range communityDirectedMultiplexQTests {
		g, weights, err := directedMultiplexFrom(test.layers)
		if err != nil {
			t.Errorf("unexpected error creating multiplex: %v", err)
			continue
		}

		const all = true

		fn := ModularMultiplexScore(g, weights, all, WeightMultiplex, 10, nil)
		p, err := Profile(fn, true, 1e-3, 0.1, 10)
		if err != nil {
			t.Errorf("%s: unexpected error: %v", test.name, err)
		}

		const tries = 1000
		for i, d := range p {
			var score float64
			for i := 0; i < tries; i++ {
				score, _ = fn(d.Low)
				if score >= d.Score {
					break
				}
			}
			if score < d.Score {
				t.Errorf("%s: failed to recover low end score: got: %v want: %v", test.name, score, d.Score)
			}
			if i != 0 && d.Score >= p[i-1].Score {
				t.Errorf("%s: not monotonically decreasing: %v -> %v", test.name, p[i-1], d)
			}
		}
	}
}
<commit_msg>graph/community: ensure exact test repeatability for examples<commit_after>// Copyright ©2016 The Gonum 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 community

import (
	"fmt"
	"log"
	"sort"
	"testing"

	"golang.org/x/exp/rand"

	"gonum.org/v1/gonum/graph"
	"gonum.org/v1/gonum/graph/internal/ordered"
	"gonum.org/v1/gonum/graph/simple"
)

func ExampleProfile_simple() {
	// Profile calls Modularize which implements the Louvain modularization algorithm.
	// Since this is a randomized algorithm we use a defined random source to ensure
	// consistency between test runs. In practice, results will not differ greatly
	// between runs with different PRNG seeds.
	src := rand.NewSource(1)

	// Create dumbell graph:
	//
	//  0       4
	//  |\     /|
	//  | 2 - 3 |
	//  |/     \|
	//  1       5
	//
	g := simple.NewUndirectedGraph()
	for u, e := range smallDumbell {
		for v := range e {
			g.SetEdge(simple.Edge{F: simple.Node(u), T: simple.Node(v)})
		}
	}

	// Get the profile of internal node weight for resolutions
	// between 0.1 and 10 using logarithmic bisection.
	p, err := Profile(ModularScore(g, Weight, 10, src), true, 1e-3, 0.1, 10)
	if err != nil {
		log.Fatal(err)
	}

	// Print out each step with communities ordered.
	for _, d := range p {
		comm := d.Communities()
		for _, c := range comm {
			sort.Sort(ordered.ByID(c))
		}
		sort.Sort(ordered.BySliceIDs(comm))
		fmt.Printf("Low:%.2v High:%.2v Score:%v Communities:%v Q=%.3v\n",
			d.Low, d.High, d.Score, comm, Q(g, comm, d.Low))
	}

	// Output:
	// Low:0.1 High:0.29 Score:14 Communities:[[0 1 2 3 4 5]] Q=0.9
	// Low:0.29 High:2.3 Score:12 Communities:[[0 1 2] [3 4 5]] Q=0.714
	// Low:2.3 High:3.5 Score:4 Communities:[[0 1] [2] [3] [4 5]] Q=-0.31
	// Low:3.5 High:10 Score:0 Communities:[[0] [1] [2] [3] [4] [5]] Q=-0.607
}

var friends, enemies *simple.WeightedUndirectedGraph

func init() {
	friends = simple.NewWeightedUndirectedGraph(0, 0)
	for u, e := range middleEast.friends {
		// Ensure unconnected nodes are included.
		if friends.Node(int64(u)) == nil {
			friends.AddNode(simple.Node(u))
		}
		for v := range e {
			friends.SetWeightedEdge(simple.WeightedEdge{F: simple.Node(u), T: simple.Node(v), W: 1})
		}
	}
	enemies = simple.NewWeightedUndirectedGraph(0, 0)
	for u, e := range middleEast.enemies {
		// Ensure unconnected nodes are included.
		if enemies.Node(int64(u)) == nil {
			enemies.AddNode(simple.Node(u))
		}
		for v := range e {
			enemies.SetWeightedEdge(simple.WeightedEdge{F: simple.Node(u), T: simple.Node(v), W: -1})
		}
	}
}

func ExampleProfile_multiplex() {
	// Profile calls ModularizeMultiplex which implements the Louvain modularization
	// algorithm. Since this is a randomized algorithm we use a defined random source
	// to ensure consistency between test runs. In practice, results will not differ
	// greatly between runs with different PRNG seeds.
	src := rand.NewSource(1)

	// The undirected graphs, friends and enemies, are the political relationships
	// in the Middle East as described in the Slate article:
	// http://www.slate.com/blogs/the_world_/2014/07/17/the_middle_east_friendship_chart.html
	g, err := NewUndirectedLayers(friends, enemies)
	if err != nil {
		log.Fatal(err)
	}
	weights := []float64{1, -1}

	// Get the profile of internal node weight for resolutions
	// between 0.1 and 10 using logarithmic bisection.
	p, err := Profile(ModularMultiplexScore(g, weights, true, WeightMultiplex, 10, src), true, 1e-3, 0.1, 10)
	if err != nil {
		log.Fatal(err)
	}

	// Print out each step with communities ordered.
	for _, d := range p {
		comm := d.Communities()
		for _, c := range comm {
			sort.Sort(ordered.ByID(c))
		}
		sort.Sort(ordered.BySliceIDs(comm))
		fmt.Printf("Low:%.2v High:%.2v Score:%v Communities:%v Q=%.3v\n",
			d.Low, d.High, d.Score, comm, QMultiplex(g, comm, weights, []float64{d.Low}))
	}

	// Output:
	// Low:0.1 High:0.72 Score:26 Communities:[[0] [1 7 9 12] [2 8 11] [3 4 5 10] [6]] Q=[24.7 1.97]
	// Low:0.72 High:1.1 Score:24 Communities:[[0 6] [1 7 9 12] [2 8 11] [3 4 5 10]] Q=[16.9 14.1]
	// Low:1.1 High:1.2 Score:18 Communities:[[0 2 6 11] [1 7 9 12] [3 4 5 8 10]] Q=[9.16 25.1]
	// Low:1.2 High:1.6 Score:10 Communities:[[0 3 4 5 6 10] [1 7 9 12] [2 8 11]] Q=[10.5 26.7]
	// Low:1.6 High:1.6 Score:8 Communities:[[0 1 6 7 9 12] [2 8 11] [3 4 5 10]] Q=[5.56 39.8]
	// Low:1.6 High:1.8 Score:2 Communities:[[0 2 3 4 5 6 10] [1 7 8 9 11 12]] Q=[-1.82 48.6]
	// Low:1.8 High:2.3 Score:-6 Communities:[[0 2 3 4 5 6 8 10 11] [1 7 9 12]] Q=[-5 57.5]
	// Low:2.3 High:2.4 Score:-10 Communities:[[0 1 2 6 7 8 9 11 12] [3 4 5 10]] Q=[-11.2 79]
	// Low:2.4 High:4.3 Score:-52 Communities:[[0 1 2 3 4 5 6 7 8 9 10 11 12]] Q=[-46.1 117]
	// Low:4.3 High:10 Score:-54 Communities:[[0 1 2 3 4 6 7 8 9 10 11 12] [5]] Q=[-82 254]
}

func TestProfileUndirected(t *testing.T) {
	for _, test := range communityUndirectedQTests {
		g := simple.NewUndirectedGraph()
		for u, e := range test.g {
			// Add nodes that are not defined by an edge.
			if g.Node(int64(u)) == nil {
				g.AddNode(simple.Node(u))
			}
			for v := range e {
				g.SetEdge(simple.Edge{F: simple.Node(u), T: simple.Node(v)})
			}
		}

		testProfileUndirected(t, test, g)
	}
}

func TestProfileWeightedUndirected(t *testing.T) {
	for _, test := range communityUndirectedQTests {
		g := simple.NewWeightedUndirectedGraph(0, 0)
		for u, e := range test.g {
			// Add nodes that are not defined by an edge.
			if g.Node(int64(u)) == nil {
				g.AddNode(simple.Node(u))
			}
			for v := range e {
				g.SetWeightedEdge(simple.WeightedEdge{F: simple.Node(u), T: simple.Node(v), W: 1})
			}
		}

		testProfileUndirected(t, test, g)
	}
}

func testProfileUndirected(t *testing.T, test communityUndirectedQTest, g graph.Undirected) {
	fn := ModularScore(g, Weight, 10, nil)
	p, err := Profile(fn, true, 1e-3, 0.1, 10)
	if err != nil {
		t.Errorf("%s: unexpected error: %v", test.name, err)
	}

	const tries = 1000
	for i, d := range p {
		var score float64
		for i := 0; i < tries; i++ {
			score, _ = fn(d.Low)
			if score >= d.Score {
				break
			}
		}
		if score < d.Score {
			t.Errorf("%s: failed to recover low end score: got: %v want: %v", test.name, score, d.Score)
		}
		if i != 0 && d.Score >= p[i-1].Score {
			t.Errorf("%s: not monotonically decreasing: %v -> %v", test.name, p[i-1], d)
		}
	}
}

func TestProfileDirected(t *testing.T) {
	for _, test := range communityDirectedQTests {
		g := simple.NewDirectedGraph()
		for u, e := range test.g {
			// Add nodes that are not defined by an edge.
			if g.Node(int64(u)) == nil {
				g.AddNode(simple.Node(u))
			}
			for v := range e {
				g.SetEdge(simple.Edge{F: simple.Node(u), T: simple.Node(v)})
			}
		}

		testProfileDirected(t, test, g)
	}
}

func TestProfileWeightedDirected(t *testing.T) {
	for _, test := range communityDirectedQTests {
		g := simple.NewWeightedDirectedGraph(0, 0)
		for u, e := range test.g {
			// Add nodes that are not defined by an edge.
			if g.Node(int64(u)) == nil {
				g.AddNode(simple.Node(u))
			}
			for v := range e {
				g.SetWeightedEdge(simple.WeightedEdge{F: simple.Node(u), T: simple.Node(v), W: 1})
			}
		}

		testProfileDirected(t, test, g)
	}
}

func testProfileDirected(t *testing.T, test communityDirectedQTest, g graph.Directed) {
	fn := ModularScore(g, Weight, 10, nil)
	p, err := Profile(fn, true, 1e-3, 0.1, 10)
	if err != nil {
		t.Errorf("%s: unexpected error: %v", test.name, err)
	}

	const tries = 1000
	for i, d := range p {
		var score float64
		for i := 0; i < tries; i++ {
			score, _ = fn(d.Low)
			if score >= d.Score {
				break
			}
		}
		if score < d.Score {
			t.Errorf("%s: failed to recover low end score: got: %v want: %v", test.name, score, d.Score)
		}
		if i != 0 && d.Score >= p[i-1].Score {
			t.Errorf("%s: not monotonically decreasing: %v -> %v", test.name, p[i-1], d)
		}
	}
}

func TestProfileUndirectedMultiplex(t *testing.T) {
	for _, test := range communityUndirectedMultiplexQTests {
		g, weights, err := undirectedMultiplexFrom(test.layers)
		if err != nil {
			t.Errorf("unexpected error creating multiplex: %v", err)
			continue
		}

		const all = true

		fn := ModularMultiplexScore(g, weights, all, WeightMultiplex, 10, nil)
		p, err := Profile(fn, true, 1e-3, 0.1, 10)
		if err != nil {
			t.Errorf("%s: unexpected error: %v", test.name, err)
		}

		const tries = 1000
		for i, d := range p {
			var score float64
			for i := 0; i < tries; i++ {
				score, _ = fn(d.Low)
				if score >= d.Score {
					break
				}
			}
			if score < d.Score {
				t.Errorf("%s: failed to recover low end score: got: %v want: %v", test.name, score, d.Score)
			}
			if i != 0 && d.Score >= p[i-1].Score {
				t.Errorf("%s: not monotonically decreasing: %v -> %v", test.name, p[i-1], d)
			}
		}
	}
}

func TestProfileDirectedMultiplex(t *testing.T) {
	for _, test := range communityDirectedMultiplexQTests {
		g, weights, err := directedMultiplexFrom(test.layers)
		if err != nil {
			t.Errorf("unexpected error creating multiplex: %v", err)
			continue
		}

		const all = true

		fn := ModularMultiplexScore(g, weights, all, WeightMultiplex, 10, nil)
		p, err := Profile(fn, true, 1e-3, 0.1, 10)
		if err != nil {
			t.Errorf("%s: unexpected error: %v", test.name, err)
		}

		const tries = 1000
		for i, d := range p {
			var score float64
			for i := 0; i < tries; i++ {
				score, _ = fn(d.Low)
				if score >= d.Score {
					break
				}
			}
			if score < d.Score {
				t.Errorf("%s: failed to recover low end score: got: %v want: %v", test.name, score, d.Score)
			}
			if i != 0 && d.Score >= p[i-1].Score {
				t.Errorf("%s: not monotonically decreasing: %v -> %v", test.name, p[i-1], d)
			}
		}
	}
}
<|endoftext|>


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

package iterator

// Defines one of the base iterators, the All iterator. Which, logically
// enough, represents all nodes or all links in the graph.
//
// This particular file is actually vestigial. It's up to the QuadStore to give
// us an All iterator that represents all things in the graph. So this is
// really the All iterator for the memstore.QuadStore. That said, it *is* one of
// the base iterators, and it helps just to see it here.

import (
	"github.com/google/cayley/graph"
)

// An All iterator across a range of int64 values, from `max` to `min`.
type Int64 struct {
	uid      uint64
	tags     graph.Tagger
	max, min int64
	at       int64
	result   graph.Value
	runstats graph.IteratorStats
}

// Creates a new Int64 with the given range.
func NewInt64(min, max int64) *Int64 {
	return &Int64{
		uid: NextUID(),
		min: min,
		max: max,
		at:  min,
	}
}

func (it *Int64) UID() uint64 {
	return it.uid
}

// Start back at the beginning
func (it *Int64) Reset() {
	it.at = it.min
}

func (it *Int64) Close() {}

func (it *Int64) Clone() graph.Iterator {
	out := NewInt64(it.min, it.max)
	out.tags.CopyFrom(it)
	return out
}

func (it *Int64) Tagger() *graph.Tagger {
	return &it.tags
}

// Fill the map based on the tags assigned to this iterator.
func (it *Int64) TagResults(dst map[string]graph.Value) {
	for _, tag := range it.tags.Tags() {
		dst[tag] = it.Result()
	}

	for tag, value := range it.tags.Fixed() {
		dst[tag] = value
	}
}

func (it *Int64) Describe() graph.Description {
	return graph.Description{
		UID:  it.UID(),
		Type: it.Type(),
		Tags: it.tags.Tags(),
	}
}

// Next() on an Int64 all iterator is a simple incrementing counter.
// Return the next integer, and mark it as the result.
func (it *Int64) Next() bool {
	graph.NextLogIn(it)
	it.runstats.Next += 1
	if it.at == -1 {
		return graph.NextLogOut(it, nil, false)
	}
	val := it.at
	it.at = it.at + 1
	if it.at > it.max {
		it.at = -1
	}
	it.result = val
	return graph.NextLogOut(it, val, true)
}

// DEPRECATED
func (it *Int64) ResultTree() *graph.ResultTree {
	return graph.NewResultTree(it.Result())
}

func (it *Int64) Result() graph.Value {
	return it.result
}

func (it *Int64) NextPath() bool {
	return false
}

// No sub-iterators.
func (it *Int64) SubIterators() []graph.Iterator {
	return nil
}

// The number of elements in an Int64 is the size of the range.
// The size is exact.
func (it *Int64) Size() (int64, bool) {
	Size := ((it.max - it.min) + 1)
	return Size, true
}

// Contains() for an Int64 is merely seeing if the passed value is
// withing the range, assuming the value is an int64.
func (it *Int64) Contains(tsv graph.Value) bool {
	graph.ContainsLogIn(it, tsv)
	it.runstats.Contains += 1
	v := tsv.(int64)
	if it.min <= v && v <= it.max {
		it.result = v
		return graph.ContainsLogOut(it, v, true)
	}
	return graph.ContainsLogOut(it, v, false)
}

// The type of this iterator is an "all". This is important, as it puts it in
// the class of "all iterators.
func (it *Int64) Type() graph.Type { return graph.All }

// There's nothing to optimize about this little iterator.
func (it *Int64) Optimize() (graph.Iterator, bool) { return it, false }

// Stats for an Int64 are simple. Super cheap to do any operation,
// and as big as the range.
func (it *Int64) Stats() graph.IteratorStats {
	s, _ := it.Size()
	return graph.IteratorStats{
		ContainsCost: 1,
		NextCost:     1,
		Size:         s,
		Next:         it.runstats.Next,
		Contains:     it.runstats.Contains,
	}
}

var _ graph.Nexter = &Int64{}
<commit_msg>Fix Err fallout for graph/iterator.Int64 iterator<commit_after>// Copyright 2014 The Cayley Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package iterator

// Defines one of the base iterators, the All iterator. Which, logically
// enough, represents all nodes or all links in the graph.
//
// This particular file is actually vestigial. It's up to the QuadStore to give
// us an All iterator that represents all things in the graph. So this is
// really the All iterator for the memstore.QuadStore. That said, it *is* one of
// the base iterators, and it helps just to see it here.

import (
	"github.com/google/cayley/graph"
)

// An All iterator across a range of int64 values, from `max` to `min`.
type Int64 struct {
	uid      uint64
	tags     graph.Tagger
	max, min int64
	at       int64
	result   graph.Value
	runstats graph.IteratorStats
}

// Creates a new Int64 with the given range.
func NewInt64(min, max int64) *Int64 {
	return &Int64{
		uid: NextUID(),
		min: min,
		max: max,
		at:  min,
	}
}

func (it *Int64) UID() uint64 {
	return it.uid
}

// Start back at the beginning
func (it *Int64) Reset() {
	it.at = it.min
}

func (it *Int64) Close() {}

func (it *Int64) Clone() graph.Iterator {
	out := NewInt64(it.min, it.max)
	out.tags.CopyFrom(it)
	return out
}

func (it *Int64) Tagger() *graph.Tagger {
	return &it.tags
}

// Fill the map based on the tags assigned to this iterator.
func (it *Int64) TagResults(dst map[string]graph.Value) {
	for _, tag := range it.tags.Tags() {
		dst[tag] = it.Result()
	}

	for tag, value := range it.tags.Fixed() {
		dst[tag] = value
	}
}

func (it *Int64) Describe() graph.Description {
	return graph.Description{
		UID:  it.UID(),
		Type: it.Type(),
		Tags: it.tags.Tags(),
	}
}

// Next() on an Int64 all iterator is a simple incrementing counter.
// Return the next integer, and mark it as the result.
func (it *Int64) Next() bool {
	graph.NextLogIn(it)
	it.runstats.Next += 1
	if it.at == -1 {
		return graph.NextLogOut(it, nil, false)
	}
	val := it.at
	it.at = it.at + 1
	if it.at > it.max {
		it.at = -1
	}
	it.result = val
	return graph.NextLogOut(it, val, true)
}

func (it *Int64) Err() error {
	// This iterator should never error.
	return nil
}

// DEPRECATED
func (it *Int64) ResultTree() *graph.ResultTree {
	return graph.NewResultTree(it.Result())
}

func (it *Int64) Result() graph.Value {
	return it.result
}

func (it *Int64) NextPath() bool {
	return false
}

// No sub-iterators.
func (it *Int64) SubIterators() []graph.Iterator {
	return nil
}

// The number of elements in an Int64 is the size of the range.
// The size is exact.
func (it *Int64) Size() (int64, bool) {
	Size := ((it.max - it.min) + 1)
	return Size, true
}

// Contains() for an Int64 is merely seeing if the passed value is
// withing the range, assuming the value is an int64.
func (it *Int64) Contains(tsv graph.Value) bool {
	graph.ContainsLogIn(it, tsv)
	it.runstats.Contains += 1
	v := tsv.(int64)
	if it.min <= v && v <= it.max {
		it.result = v
		return graph.ContainsLogOut(it, v, true)
	}
	return graph.ContainsLogOut(it, v, false)
}

// The type of this iterator is an "all". This is important, as it puts it in
// the class of "all iterators.
func (it *Int64) Type() graph.Type { return graph.All }

// There's nothing to optimize about this little iterator.
func (it *Int64) Optimize() (graph.Iterator, bool) { return it, false }

// Stats for an Int64 are simple. Super cheap to do any operation,
// and as big as the range.
func (it *Int64) Stats() graph.IteratorStats {
	s, _ := it.Size()
	return graph.IteratorStats{
		ContainsCost: 1,
		NextCost:     1,
		Size:         s,
		Next:         it.runstats.Next,
		Contains:     it.runstats.Contains,
	}
}

var _ graph.Nexter = &Int64{}
<|endoftext|>


<commit_before>// Copyright 2012, 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package openstack_test

import (
	"flag"
	"testing"

	gc "launchpad.net/gocheck"
	"launchpad.net/goose/identity"
	"launchpad.net/goose/nova"

	"launchpad.net/juju-core/provider/openstack"
)

var live = flag.Bool("live", false, "Include live OpenStack tests")

func Test(t *testing.T) {
	if *live {
		cred, err := identity.CompleteCredentialsFromEnv()
		if err != nil {
			t.Fatalf("Error setting up test suite: %s", err.Error())
		}
		registerLiveTests(cred)
	}
	registerLocalTests()
	gc.TestingT(t)
}

// localTests contains tests which do not require a live service or test double to run.
type localTests struct{}

var _ = gc.Suite(&localTests{})

// ported from lp:juju/juju/providers/openstack/tests/test_machine.py
var addressTests = []struct {
	summary  string
	private  []nova.IPAddress
	public   []nova.IPAddress
	networks []string
	expected string
	failure  error
}{
	{
		summary:  "missing",
		expected: "",
	},
	{
		summary:  "empty",
		private:  []nova.IPAddress{},
		networks: []string{"private"},
		expected: "",
	},
	{
		summary:  "private only",
		private:  []nova.IPAddress{{4, "192.168.0.1"}},
		networks: []string{"private"},
		expected: "192.168.0.1",
	},
	{
		summary:  "private plus (HP cloud)",
		private:  []nova.IPAddress{{4, "192.168.0.1"}, {4, "10.0.0.1"}},
		networks: []string{"private"},
		expected: "192.168.0.1", // first cloud-local/unknown
	},
	{
		summary:  "public only",
		public:   []nova.IPAddress{{4, "8.8.8.8"}},
		networks: []string{"", "public"},
		expected: "8.8.8.8",
	},
	{
		summary:  "public and private",
		private:  []nova.IPAddress{{4, "10.0.0.4"}},
		public:   []nova.IPAddress{{4, "8.8.4.4"}},
		networks: []string{"private", "public"},
		expected: "8.8.4.4",
	},
	{
		summary:  "public private plus",
		private:  []nova.IPAddress{{4, "127.0.0.4"}, {4, "192.168.0.1"}},
		public:   []nova.IPAddress{{4, "8.8.8.8"}},
		networks: []string{"private", "public"},
		expected: "8.8.8.8",
	},
	{
		summary:  "custom only",
		private:  []nova.IPAddress{{4, "192.168.0.1"}},
		networks: []string{"special"},
		expected: "192.168.0.1",
	},
	{
		summary:  "custom and public",
		private:  []nova.IPAddress{{4, "172.16.0.1"}},
		public:   []nova.IPAddress{{4, "8.8.8.8"}},
		networks: []string{"special", "public"},
		expected: "8.8.8.8",
	},
	{
		summary:  "non-IPv4",
		private:  []nova.IPAddress{{6, "::dead:beef:f00d"}},
		networks: []string{"private"},
		expected: "",
	},
}

func (t *localTests) TestGetServerAddresses(c *gc.C) {
	for i, t := range addressTests {
		c.Logf("#%d. %s -> %s (%v)", i, t.summary, t.expected, t.failure)
		addresses := make(map[string][]nova.IPAddress)
		if t.private != nil {
			if len(t.networks) < 1 {
				addresses["private"] = t.private
			} else {
				addresses[t.networks[0]] = t.private
			}
		}
		if t.public != nil {
			if len(t.networks) < 2 {
				addresses["public"] = t.public
			} else {
				addresses[t.networks[1]] = t.public
			}
		}
		addr := openstack.InstanceAddress(addresses)
		c.Assert(addr, gc.Equals, t.expected)
	}
}
<commit_msg>provider/openstack: change test to do what it says<commit_after>// Copyright 2012, 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package openstack_test

import (
	"flag"
	"testing"

	gc "launchpad.net/gocheck"
	"launchpad.net/goose/identity"
	"launchpad.net/goose/nova"

	"launchpad.net/juju-core/provider/openstack"
)

var live = flag.Bool("live", false, "Include live OpenStack tests")

func Test(t *testing.T) {
	if *live {
		cred, err := identity.CompleteCredentialsFromEnv()
		if err != nil {
			t.Fatalf("Error setting up test suite: %s", err.Error())
		}
		registerLiveTests(cred)
	}
	registerLocalTests()
	gc.TestingT(t)
}

// localTests contains tests which do not require a live service or test double to run.
type localTests struct{}

var _ = gc.Suite(&localTests{})

// ported from lp:juju/juju/providers/openstack/tests/test_machine.py
var addressTests = []struct {
	summary  string
	private  []nova.IPAddress
	public   []nova.IPAddress
	networks []string
	expected string
	failure  error
}{
	{
		summary:  "missing",
		expected: "",
	},
	{
		summary:  "empty",
		private:  []nova.IPAddress{},
		networks: []string{"private"},
		expected: "",
	},
	{
		summary:  "private only",
		private:  []nova.IPAddress{{4, "192.168.0.1"}},
		networks: []string{"private"},
		expected: "192.168.0.1",
	},
	{
		summary:  "private plus (HP cloud)",
		private:  []nova.IPAddress{{4, "10.0.0.1"}, {4, "8.8.4.4"}},
		networks: []string{"private"},
		expected: "8.8.4.4",
	},
	{
		summary:  "public only",
		public:   []nova.IPAddress{{4, "8.8.8.8"}},
		networks: []string{"", "public"},
		expected: "8.8.8.8",
	},
	{
		summary:  "public and private",
		private:  []nova.IPAddress{{4, "10.0.0.4"}},
		public:   []nova.IPAddress{{4, "8.8.4.4"}},
		networks: []string{"private", "public"},
		expected: "8.8.4.4",
	},
	{
		summary:  "public private plus",
		private:  []nova.IPAddress{{4, "127.0.0.4"}, {4, "192.168.0.1"}},
		public:   []nova.IPAddress{{4, "8.8.8.8"}},
		networks: []string{"private", "public"},
		expected: "8.8.8.8",
	},
	{
		summary:  "custom only",
		private:  []nova.IPAddress{{4, "192.168.0.1"}},
		networks: []string{"special"},
		expected: "192.168.0.1",
	},
	{
		summary:  "custom and public",
		private:  []nova.IPAddress{{4, "172.16.0.1"}},
		public:   []nova.IPAddress{{4, "8.8.8.8"}},
		networks: []string{"special", "public"},
		expected: "8.8.8.8",
	},
	{
		summary:  "non-IPv4",
		private:  []nova.IPAddress{{6, "::dead:beef:f00d"}},
		networks: []string{"private"},
		expected: "",
	},
}

func (t *localTests) TestGetServerAddresses(c *gc.C) {
	for i, t := range addressTests {
		c.Logf("#%d. %s -> %s (%v)", i, t.summary, t.expected, t.failure)
		addresses := make(map[string][]nova.IPAddress)
		if t.private != nil {
			if len(t.networks) < 1 {
				addresses["private"] = t.private
			} else {
				addresses[t.networks[0]] = t.private
			}
		}
		if t.public != nil {
			if len(t.networks) < 2 {
				addresses["public"] = t.public
			} else {
				addresses[t.networks[1]] = t.public
			}
		}
		addr := openstack.InstanceAddress(addresses)
		c.Assert(addr, gc.Equals, t.expected)
	}
}
<|endoftext|>


<commit_before>package schema

import (
	"fmt"
	"reflect"
	"strings"
	"sync"

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

// newValueWriter is a minor re-implementation of MapFieldWriter to include
// keys that should be marked as computed, to represent the new part of a
// pseudo-diff.
type newValueWriter struct {
	*MapFieldWriter

	// A list of keys that should be marked as computed.
	computedKeys map[string]bool

	// A lock to prevent races on writes. The underlying writer will have one as
	// well - this is for computed keys.
	lock sync.Mutex
}

// WriteField overrides MapValueWriter's WriteField, adding the ability to flag
// the address as computed.
func (w *newValueWriter) WriteField(address []string, value interface{}, computed bool) error {
	if err := w.MapFieldWriter.WriteField(address, value); err != nil {
		return err
	}

	w.lock.Lock()
	defer w.lock.Unlock()
	if w.result == nil {
		w.computedKeys = make(map[string]bool)
	}

	if computed {
		w.computedKeys[strings.Join(address, ".")] = true
	}
	return nil
}

// ComputedKeysMap returns the underlying computed keys map.
func (w *newValueWriter) ComputedKeysMap() map[string]bool {
	w.lock.Lock()
	defer w.lock.Unlock()
	if w.result == nil {
		w.computedKeys = make(map[string]bool)
	}
	return w.computedKeys
}

// newValueReader is a minor re-implementation of MapFieldReader and is the
// read counterpart to MapValueWriter, allowing the read of keys flagged as
// computed to accommodate the diff override logic in ResourceDiff.
type newValueReader struct {
	*MapFieldReader

	// The list of computed keys from a newValueWriter.
	computedKeys map[string]bool
}

// ReadField reads the values from the underlying writer, returning the
// computed value if it is found as well.
func (r *newValueReader) ReadField(address []string) (FieldReadResult, error) {
	v, err := r.MapFieldReader.ReadField(address)
	if err != nil {
		return FieldReadResult{}, err
	}
	if _, ok := r.computedKeys[strings.Join(address, ".")]; ok {
		v.Computed = true
	}

	return v, nil
}

// ResourceDiff is used to query and make custom changes to an in-flight diff.
// It can be used to veto particular changes in the diff, customize the diff
// that has been created, or diff values not controlled by config.
//
// The object functions similar to ResourceData, however most notably lacks
// Set, SetPartial, and Partial, as it should be used to change diff values
// only.  Most other frist-class ResourceData functions exist, namely Get,
// GetOk, HasChange, and GetChange exist.
//
// All functions in ResourceDiff, save for ForceNew, can only be used on
// computed fields.
type ResourceDiff struct {
	// The schema for the resource being worked on.
	schema map[string]*Schema

	// The current config for this resource.
	config *terraform.ResourceConfig

	// The state for this resource as it exists post-refresh, after the initial
	// diff.
	state *terraform.InstanceState

	// The diff created by Terraform. This diff is used, along with state,
	// config, and custom-set diff data, to provide a multi-level reader
	// experience similar to ResourceData.
	diff *terraform.InstanceDiff

	// The internal reader structure that contains the state, config, the default
	// diff, and the new diff.
	multiReader *MultiLevelFieldReader

	// A writer that writes overridden old fields.
	oldWriter *MapFieldWriter

	// A writer that writes overridden new fields.
	newWriter *newValueWriter
}

// newResourceDiff creates a new ResourceDiff instance.
func newResourceDiff(schema map[string]*Schema, config *terraform.ResourceConfig, state *terraform.InstanceState, diff *terraform.InstanceDiff) *ResourceDiff {
	d := &ResourceDiff{
		config: config,
		state:  state,
		diff:   diff,
	}
	// Duplicate the passed in schema to ensure that any changes we make with
	// functions like ForceNew don't affect the referenced schema.
	for k, v := range schema {
		newSchema := *v
		d.schema[k] = &newSchema
	}

	d.oldWriter = &MapFieldWriter{Schema: d.schema}
	d.newWriter = &newValueWriter{
		MapFieldWriter: &MapFieldWriter{Schema: d.schema},
	}
	readers := make(map[string]FieldReader)
	var stateAttributes map[string]string
	if d.state != nil {
		stateAttributes = d.state.Attributes
		readers["state"] = &MapFieldReader{
			Schema: d.schema,
			Map:    BasicMapReader(stateAttributes),
		}
	}
	if d.config != nil {
		readers["config"] = &ConfigFieldReader{
			Schema: d.schema,
			Config: d.config,
		}
	}
	if d.diff != nil {
		readers["diff"] = &DiffFieldReader{
			Schema: d.schema,
			Diff:   d.diff,
			Source: &MultiLevelFieldReader{
				Levels:  []string{"state", "config"},
				Readers: readers,
			},
		}
	}
	readers["newDiffOld"] = &MapFieldReader{
		Schema: d.schema,
		Map:    BasicMapReader(d.oldWriter.Map()),
	}
	readers["newDiffNew"] = &newValueReader{
		MapFieldReader: &MapFieldReader{
			Schema: d.schema,
			Map:    BasicMapReader(d.newWriter.Map()),
		},
		computedKeys: d.newWriter.ComputedKeysMap(),
	}
	d.multiReader = &MultiLevelFieldReader{
		Levels: []string{
			"state",
			"config",
			"diff",
			"newDiffOld",
			"newDiffNew",
		},

		Readers: readers,
	}

	return d
}

// ClearAll wipes the current diff. This cannot be undone - use only if you
// need to create a whole new diff from scatch, such as when you are leaning on
// the provider completely to create the diff.
func (d *ResourceDiff) ClearAll() {
	d.diff = new(terraform.InstanceDiff)
}

// diffChange helps to implement resourceDiffer and derives its change values
// from ResourceDiff's own change data, in addition to existing diff, config, and state.
func (d *ResourceDiff) diffChange(key string) (interface{}, interface{}, bool, bool) {
	old, new := d.getChange(key)

	if !old.Exists {
		old.Value = nil
	}
	if !new.Exists {
		new.Value = nil
	}

	return old.Value, new.Value, !reflect.DeepEqual(old.Value, new.Value), new.Computed
}

// SetNew is used to set a new diff value for the mentioned key. The value must
// be correct for the attribute's schema (mostly relevant for maps, lists, and
// sets). The original value from the state is used as the old value.
//
// This function is only allowed on computed attributes.
//
// It is an unsupported operation to set invalid values with this function -
// doing so will taint any existing diff for this key and will remove it from
// the catalog.
func (d *ResourceDiff) SetNew(key string, value interface{}) error {
	return d.SetDiff(key, d.Get(key), value, false)
}

// SetNewComputed functions like SetNew, except that it sets the new value to
// the zero value and flags the attribute's diff as computed.
//
// This function is only allowed on computed keys.
func (d *ResourceDiff) SetNewComputed(key string) error {
	return d.SetDiff(key, d.Get(key), d.schema[key].ZeroValue(), true)
}

// SetDiff allows the setting of both old and new values for the diff
// referenced by a given key. This can be used to completely override
// Terraform's own diff behaviour, and can be used in conjunction with Clear or
// ClearAll to construct a compleletely new diff based off of provider logic
// alone.
//
// This function is only allowed on computed keys.
func (d *ResourceDiff) SetDiff(key string, old, new interface{}, computed bool) error {
	if !d.schema[key].Computed {
		return fmt.Errorf("SetNew, SetNewComputed, and SetDiff are allowed on computed attributes only - %s is not one", key)
	}

	if err := d.oldWriter.WriteField(strings.Split(key, "."), old); err != nil {
		return fmt.Errorf("Cannot set old diff value for key %s: %s", key, err)
	}

	if err := d.newWriter.WriteField(strings.Split(key, "."), new, computed); err != nil {
		return fmt.Errorf("Cannot set new diff value for key %s: %s", key, err)
	}

	return nil
}

// ForceNew force-flags ForceNew in the schema for a specific key, and
// re-calculates its diff. This function is a no-op/error if there is no diff.
//
// Note that the change to schema is permanent for the lifecycle of this
// specific ResourceDiff instance, until ClearAll or Reset is called to start
// anew.
func (d *ResourceDiff) ForceNew(key string) error {
	if !d.HasChange(key) {
		return fmt.Errorf("ResourceDiff.ForceNew: No changes for %s", key)
	}

	old, new := d.GetChange(key)
	d.schema[key].ForceNew = true
	return d.SetDiff(key, old, new, false)
}

// Get hands off to ResourceData.Get.
func (d *ResourceDiff) Get(key string) interface{} {
	r, _ := d.GetOk(key)
	return r
}

// GetChange gets the change between the state and diff, checking first to see
// if a overridden diff exists.
//
// This implementation differs from ResourceData's in the way that we first get
// results from the exact levels for the new diff, then from state and diff as
// per normal.
func (d *ResourceDiff) GetChange(key string) (interface{}, interface{}) {
	old, new := d.getChange(key)
	return old.Value, new.Value
}

// GetOk functions the same way as ResourceData.GetOk, but it also checks the
// new diff levels to provide data consistent with the current state of the
// customized diff.
func (d *ResourceDiff) GetOk(key string) (interface{}, bool) {
	r := d.get(strings.Split(key, "."), "newDiffNew")
	exists := r.Exists && !r.Computed
	if exists {
		// If it exists, we also want to verify it is not the zero-value.
		value := r.Value
		zero := r.Schema.Type.Zero()

		if eq, ok := value.(Equal); ok {
			exists = !eq.Equal(zero)
		} else {
			exists = !reflect.DeepEqual(value, zero)
		}
	}

	return r.Value, exists
}

// HasChange checks to see if there is a change between state and the diff, or
// in the overridden diff.
func (d *ResourceDiff) HasChange(key string) bool {
	old, new := d.GetChange(key)

	// If the type implements the Equal interface, then call that
	// instead of just doing a reflect.DeepEqual. An example where this is
	// needed is *Set
	if eq, ok := old.(Equal); ok {
		return !eq.Equal(new)
	}

	return !reflect.DeepEqual(old, new)
}

// Id returns the ID of this resource.
//
// Note that technically, ID does not change during diffs (it either has
// already changed in the refresh, or will change on update), hence we do not
// support updating the ID or fetching it from anything else other than state.
func (d *ResourceDiff) Id() string {
	var result string

	if d.state != nil {
		result = d.state.ID
	}
	return result
}

// getChange gets values from two different levels, designed for use in
// diffChange, HasChange, and GetChange.
//
// This implementation differs from ResourceData's in the way that we first get
// results from the exact levels for the new diff, then from state and diff as
// per normal.
func (d *ResourceDiff) getChange(key string) (getResult, getResult) {
	old := d.getExact(strings.Split(key, "."), "newDiffOld")
	new := d.getExact(strings.Split(key, "."), "newDiffNew")

	if old.Exists && new.Exists {
		// Both values should exist if SetDiff operated on this key.
		// TODO: Maybe verify this. Zero values might be an issue here.
		return old, new
	}

	// If we haven't set this in the new diff, then we want to get the default
	// levels as if we were using ResourceData normally.
	old = d.get(strings.Split(key, "."), "state")
	new = d.get(strings.Split(key, "."), "diff")
	return old, new
}

// get performs the appropriate multi-level reader logic for ResourceDiff,
// starting at source. Refer to newResourceDiff for the level order.
func (d *ResourceDiff) get(addr []string, source string) getResult {
	result, err := d.multiReader.ReadFieldMerge(addr, source)
	if err != nil {
		panic(err)
	}

	return d.finalizeResult(addr, result)
}

// getExact gets an attribute from the exact level referenced by source.
func (d *ResourceDiff) getExact(addr []string, source string) getResult {
	result, err := d.multiReader.ReadFieldExact(addr, source)
	if err != nil {
		panic(err)
	}

	return d.finalizeResult(addr, result)
}

// finalizeResult does some post-processing of the result produced by get and getExact.
func (d *ResourceDiff) finalizeResult(addr []string, result FieldReadResult) getResult {
	// If the result doesn't exist, then we set the value to the zero value
	var schema *Schema
	if schemaL := addrToSchema(addr, d.schema); len(schemaL) > 0 {
		schema = schemaL[len(schemaL)-1]
	}

	if result.Value == nil && schema != nil {
		result.Value = result.ValueOrZero(schema)
	}

	// Transform the FieldReadResult into a getResult. It might be worth
	// merging these two structures one day.
	return getResult{
		Value:          result.Value,
		ValueProcessed: result.ValueProcessed,
		Computed:       result.Computed,
		Exists:         result.Exists,
		Schema:         schema,
	}
}
<commit_msg>helper/schema: Add Clear function to ResourceDiff<commit_after>package schema

import (
	"fmt"
	"reflect"
	"strings"
	"sync"

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

// newValueWriter is a minor re-implementation of MapFieldWriter to include
// keys that should be marked as computed, to represent the new part of a
// pseudo-diff.
type newValueWriter struct {
	*MapFieldWriter

	// A list of keys that should be marked as computed.
	computedKeys map[string]bool

	// A lock to prevent races on writes. The underlying writer will have one as
	// well - this is for computed keys.
	lock sync.Mutex
}

// WriteField overrides MapValueWriter's WriteField, adding the ability to flag
// the address as computed.
func (w *newValueWriter) WriteField(address []string, value interface{}, computed bool) error {
	if err := w.MapFieldWriter.WriteField(address, value); err != nil {
		return err
	}

	w.lock.Lock()
	defer w.lock.Unlock()
	if w.result == nil {
		w.computedKeys = make(map[string]bool)
	}

	if computed {
		w.computedKeys[strings.Join(address, ".")] = true
	}
	return nil
}

// ComputedKeysMap returns the underlying computed keys map.
func (w *newValueWriter) ComputedKeysMap() map[string]bool {
	w.lock.Lock()
	defer w.lock.Unlock()
	if w.result == nil {
		w.computedKeys = make(map[string]bool)
	}
	return w.computedKeys
}

// newValueReader is a minor re-implementation of MapFieldReader and is the
// read counterpart to MapValueWriter, allowing the read of keys flagged as
// computed to accommodate the diff override logic in ResourceDiff.
type newValueReader struct {
	*MapFieldReader

	// The list of computed keys from a newValueWriter.
	computedKeys map[string]bool
}

// ReadField reads the values from the underlying writer, returning the
// computed value if it is found as well.
func (r *newValueReader) ReadField(address []string) (FieldReadResult, error) {
	v, err := r.MapFieldReader.ReadField(address)
	if err != nil {
		return FieldReadResult{}, err
	}
	if _, ok := r.computedKeys[strings.Join(address, ".")]; ok {
		v.Computed = true
	}

	return v, nil
}

// ResourceDiff is used to query and make custom changes to an in-flight diff.
// It can be used to veto particular changes in the diff, customize the diff
// that has been created, or diff values not controlled by config.
//
// The object functions similar to ResourceData, however most notably lacks
// Set, SetPartial, and Partial, as it should be used to change diff values
// only.  Most other frist-class ResourceData functions exist, namely Get,
// GetOk, HasChange, and GetChange exist.
//
// All functions in ResourceDiff, save for ForceNew, can only be used on
// computed fields.
type ResourceDiff struct {
	// The schema for the resource being worked on.
	schema map[string]*Schema

	// The current config for this resource.
	config *terraform.ResourceConfig

	// The state for this resource as it exists post-refresh, after the initial
	// diff.
	state *terraform.InstanceState

	// The diff created by Terraform. This diff is used, along with state,
	// config, and custom-set diff data, to provide a multi-level reader
	// experience similar to ResourceData.
	diff *terraform.InstanceDiff

	// The internal reader structure that contains the state, config, the default
	// diff, and the new diff.
	multiReader *MultiLevelFieldReader

	// A writer that writes overridden old fields.
	oldWriter *MapFieldWriter

	// A writer that writes overridden new fields.
	newWriter *newValueWriter
}

// newResourceDiff creates a new ResourceDiff instance.
func newResourceDiff(schema map[string]*Schema, config *terraform.ResourceConfig, state *terraform.InstanceState, diff *terraform.InstanceDiff) *ResourceDiff {
	d := &ResourceDiff{
		config: config,
		state:  state,
		diff:   diff,
	}
	// Duplicate the passed in schema to ensure that any changes we make with
	// functions like ForceNew don't affect the referenced schema.
	for k, v := range schema {
		newSchema := *v
		d.schema[k] = &newSchema
	}

	d.oldWriter = &MapFieldWriter{Schema: d.schema}
	d.newWriter = &newValueWriter{
		MapFieldWriter: &MapFieldWriter{Schema: d.schema},
	}
	readers := make(map[string]FieldReader)
	var stateAttributes map[string]string
	if d.state != nil {
		stateAttributes = d.state.Attributes
		readers["state"] = &MapFieldReader{
			Schema: d.schema,
			Map:    BasicMapReader(stateAttributes),
		}
	}
	if d.config != nil {
		readers["config"] = &ConfigFieldReader{
			Schema: d.schema,
			Config: d.config,
		}
	}
	if d.diff != nil {
		readers["diff"] = &DiffFieldReader{
			Schema: d.schema,
			Diff:   d.diff,
			Source: &MultiLevelFieldReader{
				Levels:  []string{"state", "config"},
				Readers: readers,
			},
		}
	}
	readers["newDiffOld"] = &MapFieldReader{
		Schema: d.schema,
		Map:    BasicMapReader(d.oldWriter.Map()),
	}
	readers["newDiffNew"] = &newValueReader{
		MapFieldReader: &MapFieldReader{
			Schema: d.schema,
			Map:    BasicMapReader(d.newWriter.Map()),
		},
		computedKeys: d.newWriter.ComputedKeysMap(),
	}
	d.multiReader = &MultiLevelFieldReader{
		Levels: []string{
			"state",
			"config",
			"diff",
			"newDiffOld",
			"newDiffNew",
		},

		Readers: readers,
	}

	return d
}

// ClearAll wipes the current diff. This cannot be undone - use only if you
// need to create a whole new diff from scatch, such as when you are leaning on
// the provider completely to create the diff.
//
// Note that this does not wipe overrides.
func (d *ResourceDiff) ClearAll() {
	d.diff = new(terraform.InstanceDiff)
}

// Clear wipes the diff for a particular key. It is called by SetDiff to remove
// any possibility of conflicts, but can be called on its own to just remove a
// specific key from the diff completely.
//
// Note that this does not wipe an override.
func (d *ResourceDiff) Clear(key string) error {
	// Check the schema to make sure that this key exists first.
	if _, ok := d.schema[key]; !ok {
		return fmt.Errorf("%s is not a valid key", key)
	}
	for k := range d.diff.Attributes {
		if strings.HasPrefix(k, key) {
			delete(d.diff.Attributes, k)
		}
	}
	return nil
}

// diffChange helps to implement resourceDiffer and derives its change values
// from ResourceDiff's own change data, in addition to existing diff, config, and state.
func (d *ResourceDiff) diffChange(key string) (interface{}, interface{}, bool, bool) {
	old, new := d.getChange(key)

	if !old.Exists {
		old.Value = nil
	}
	if !new.Exists {
		new.Value = nil
	}

	return old.Value, new.Value, !reflect.DeepEqual(old.Value, new.Value), new.Computed
}

// SetNew is used to set a new diff value for the mentioned key. The value must
// be correct for the attribute's schema (mostly relevant for maps, lists, and
// sets). The original value from the state is used as the old value.
//
// This function is only allowed on computed attributes.
//
// It is an unsupported operation to set invalid values with this function -
// doing so will taint any existing diff for this key and will remove it from
// the catalog.
func (d *ResourceDiff) SetNew(key string, value interface{}) error {
	return d.SetDiff(key, d.Get(key), value, false)
}

// SetNewComputed functions like SetNew, except that it sets the new value to
// the zero value and flags the attribute's diff as computed.
//
// This function is only allowed on computed keys.
func (d *ResourceDiff) SetNewComputed(key string) error {
	return d.SetDiff(key, d.Get(key), d.schema[key].ZeroValue(), true)
}

// SetDiff allows the setting of both old and new values for the diff
// referenced by a given key. This can be used to completely override
// Terraform's own diff behaviour, and can be used in conjunction with Clear or
// ClearAll to construct a compleletely new diff based off of provider logic
// alone.
//
// This function is only allowed on computed keys.
func (d *ResourceDiff) SetDiff(key string, old, new interface{}, computed bool) error {
	if !d.schema[key].Computed {
		return fmt.Errorf("SetNew, SetNewComputed, and SetDiff are allowed on computed attributes only - %s is not one", key)
	}

	if err := d.Clear(key); err != nil {
		return err
	}

	if err := d.oldWriter.WriteField(strings.Split(key, "."), old); err != nil {
		return fmt.Errorf("Cannot set old diff value for key %s: %s", key, err)
	}

	if err := d.newWriter.WriteField(strings.Split(key, "."), new, computed); err != nil {
		return fmt.Errorf("Cannot set new diff value for key %s: %s", key, err)
	}

	return nil
}

// ForceNew force-flags ForceNew in the schema for a specific key, and
// re-calculates its diff. This function is a no-op/error if there is no diff.
//
// Note that the change to schema is permanent for the lifecycle of this
// specific ResourceDiff instance, until ClearAll or Reset is called to start
// anew.
func (d *ResourceDiff) ForceNew(key string) error {
	if !d.HasChange(key) {
		return fmt.Errorf("ResourceDiff.ForceNew: No changes for %s", key)
	}

	old, new := d.GetChange(key)
	d.schema[key].ForceNew = true
	return d.SetDiff(key, old, new, false)
}

// Get hands off to ResourceData.Get.
func (d *ResourceDiff) Get(key string) interface{} {
	r, _ := d.GetOk(key)
	return r
}

// GetChange gets the change between the state and diff, checking first to see
// if a overridden diff exists.
//
// This implementation differs from ResourceData's in the way that we first get
// results from the exact levels for the new diff, then from state and diff as
// per normal.
func (d *ResourceDiff) GetChange(key string) (interface{}, interface{}) {
	old, new := d.getChange(key)
	return old.Value, new.Value
}

// GetOk functions the same way as ResourceData.GetOk, but it also checks the
// new diff levels to provide data consistent with the current state of the
// customized diff.
func (d *ResourceDiff) GetOk(key string) (interface{}, bool) {
	r := d.get(strings.Split(key, "."), "newDiffNew")
	exists := r.Exists && !r.Computed
	if exists {
		// If it exists, we also want to verify it is not the zero-value.
		value := r.Value
		zero := r.Schema.Type.Zero()

		if eq, ok := value.(Equal); ok {
			exists = !eq.Equal(zero)
		} else {
			exists = !reflect.DeepEqual(value, zero)
		}
	}

	return r.Value, exists
}

// HasChange checks to see if there is a change between state and the diff, or
// in the overridden diff.
func (d *ResourceDiff) HasChange(key string) bool {
	old, new := d.GetChange(key)

	// If the type implements the Equal interface, then call that
	// instead of just doing a reflect.DeepEqual. An example where this is
	// needed is *Set
	if eq, ok := old.(Equal); ok {
		return !eq.Equal(new)
	}

	return !reflect.DeepEqual(old, new)
}

// Id returns the ID of this resource.
//
// Note that technically, ID does not change during diffs (it either has
// already changed in the refresh, or will change on update), hence we do not
// support updating the ID or fetching it from anything else other than state.
func (d *ResourceDiff) Id() string {
	var result string

	if d.state != nil {
		result = d.state.ID
	}
	return result
}

// getChange gets values from two different levels, designed for use in
// diffChange, HasChange, and GetChange.
//
// This implementation differs from ResourceData's in the way that we first get
// results from the exact levels for the new diff, then from state and diff as
// per normal.
func (d *ResourceDiff) getChange(key string) (getResult, getResult) {
	old := d.getExact(strings.Split(key, "."), "newDiffOld")
	new := d.getExact(strings.Split(key, "."), "newDiffNew")

	if old.Exists && new.Exists {
		// Both values should exist if SetDiff operated on this key.
		// TODO: Maybe verify this. Zero values might be an issue here.
		return old, new
	}

	// If we haven't set this in the new diff, then we want to get the default
	// levels as if we were using ResourceData normally.
	old = d.get(strings.Split(key, "."), "state")
	new = d.get(strings.Split(key, "."), "diff")
	return old, new
}

// get performs the appropriate multi-level reader logic for ResourceDiff,
// starting at source. Refer to newResourceDiff for the level order.
func (d *ResourceDiff) get(addr []string, source string) getResult {
	result, err := d.multiReader.ReadFieldMerge(addr, source)
	if err != nil {
		panic(err)
	}

	return d.finalizeResult(addr, result)
}

// getExact gets an attribute from the exact level referenced by source.
func (d *ResourceDiff) getExact(addr []string, source string) getResult {
	result, err := d.multiReader.ReadFieldExact(addr, source)
	if err != nil {
		panic(err)
	}

	return d.finalizeResult(addr, result)
}

// finalizeResult does some post-processing of the result produced by get and getExact.
func (d *ResourceDiff) finalizeResult(addr []string, result FieldReadResult) getResult {
	// If the result doesn't exist, then we set the value to the zero value
	var schema *Schema
	if schemaL := addrToSchema(addr, d.schema); len(schemaL) > 0 {
		schema = schemaL[len(schemaL)-1]
	}

	if result.Value == nil && schema != nil {
		result.Value = result.ValueOrZero(schema)
	}

	// Transform the FieldReadResult into a getResult. It might be worth
	// merging these two structures one day.
	return getResult{
		Value:          result.Value,
		ValueProcessed: result.ValueProcessed,
		Computed:       result.Computed,
		Exists:         result.Exists,
		Schema:         schema,
	}
}
<|endoftext|>