<commit_before>// Copyright 2014 Arash Payan. All rights reserved.
// Use of this source code is governed by the Apache 2
// license that can be found in the LICENSE file.

// Package apns provides a client for using the Apple Push Notification service.
package apns

import (
	"bytes"
	"container/list"
	"crypto/tls"
	"encoding/binary"
	"encoding/hex"
	"encoding/json"
	"log"
	"math/rand"
	"net"
	"sync"
	"time"
)

// ProductionGateway is the host address and port to pass NewClient for
// connecting to the production APNs.
const ProductionGateway = "gateway.push.apple.com:2195"

// SandboxGateway is the host address and port to pass NewClient for
// connecting to the development environment for the APNs.
const SandboxGateway = "gateway.sandbox.push.apple.com:2195"

// MaxPushNotificationBytes is the maximum number of bytes permitted in a payload
// sent to the APNs
const MaxPushNotificationBytes = 2048

// Constants related to the payload fields and their lengths.
const (
	deviceTokenItemID            = 1
	payloadItemID                = 2
	notificationIdentifierItemID = 3
	expirationDateItemID         = 4
	priorityItemID               = 5
	deviceTokenLength            = 32
	notificationIdentifierLength = 4
	expirationDateLength         = 4
	priorityLength               = 1
)

// initialized in init()
var apnsErrors map[int]string

// Push commands always start with command value 2.
const pushCommandValue = 2

// gRecentNotifications stores recently sent notifications in case an error
// is sent by Apple.
var gRecents struct {
	sync.Mutex
	notifications *list.List
}

func init() {
	gRecents.notifications = list.New()
}

// Notification represents a push notification for a specific iOS device
type Notification struct {
	Alert       string      `json:"alert"`
	Badge       int16       `json:"badge"`
	Sound       string      `json:"sound"`
	AppData     interface{} `json:"-"`
	deviceToken string
	identifier  int32
	expiry      uint32
	priority    uint8
}

// APNs error values sent from Apple when a notification has an error.
const (
	processingErrorID    = 1
	missingDeviceTokenID = 2
	missingTopicID       = 3
	missingPayloadID     = 4
	invalidTokenSizeID   = 5
	invalidTopicSizeID   = 6
	invalidPayloadSizeID = 7
	invalidTokenID       = 8
	shutdownID           = 9
	noErrorID            = 10
)

func init() {
	apnsErrors = make(map[int]string)
	apnsErrors[0] = "No errors encountered"
	apnsErrors[processingErrorID] = "Processing error"
	apnsErrors[missingDeviceTokenID] = "Missing device token"
	apnsErrors[missingTopicID] = "Missing topic"
	apnsErrors[missingPayloadID] = "Missing payload"
	apnsErrors[invalidTokenSizeID] = "Invalid token size"
	apnsErrors[invalidTopicSizeID] = "Invalid topic size"
	apnsErrors[invalidPayloadSizeID] = "Invalid payload size"
	apnsErrors[invalidTokenID] = "Invalid token"
	apnsErrors[shutdownID] = "Shutdown"
	apnsErrors[noErrorID] = "None (unknown)"
}

// NewNotification creates an APNs notification that can be delivered to an iOS
// device with the specified devToken.
func NewNotification(devToken string) *Notification {
	n := &Notification{deviceToken: devToken}
	n.identifier = rand.New(rand.NewSource(time.Now().UnixNano())).Int31()
	return n
}

// Client is the broker between an APNs provider and the gateway
type Client struct {
	Gateway             string
	CertificateFile     string
	KeyFile             string
	conn                *tls.Conn
	IsConnected         bool
	notificationChan    chan *Notification
	invalidTokenHandler func(string)
}

// NewClient initializes a Client struct that you can use to send Notifications
// to the APNs. gateway should be either ProductionGateway or SandboxGateway.
// certFile and keyFile are paths to your Apple signed certificate and private
// key. invalidTokenHandler is a function that will be called when an attempt
// send a notification results in an invalid token error from the APNs. When
// you receive these errors, you should remove/disassociate the device token from
// your database/user. This function callback is provided as a simpler alternative
// to implementing a feedback service that would periodically poll Apple for
// invalid tokens.
func NewClient(gateway, certFile, keyFile string, invalidTokenHandler func(string)) *Client {
	c := Client{Gateway: gateway, CertificateFile: certFile, KeyFile: keyFile}
	c.notificationChan = make(chan *Notification, 4096) // 4096 oughtta be enough for anybody
	c.invalidTokenHandler = invalidTokenHandler
	return &c
}

func (c *Client) connect() error {
	c.IsConnected = false
	cert, err := tls.LoadX509KeyPair(c.CertificateFile, c.KeyFile)
	if err != nil {
		return err
	}

	host, _, err := net.SplitHostPort(c.Gateway)
	conf := &tls.Config{
		Certificates: []tls.Certificate{cert},
		ServerName:   host,
	}

	c.conn, err = tls.Dial("tcp", c.Gateway, conf)
	if err != nil {
		return err
	}
	c.IsConnected = true
	return nil
}

// Connect establishes a connection to the APNs gateway specified in NewClient().
// This method blocks until the connection is established.
func (c *Client) Connect() error {
	err := c.connect()
	if err != nil {
		return err
	}

	// start a goroutine to read this connection for any errors messages
	go c.readErrors()

	// start a goroutine to process the queue of notifications
	go c.processQueue()

	return nil
}

func (c *Client) reconnect() {
	log.Print("Attempting to reconnect to APNs")
	maxSleepTime := 60 * time.Second
	// attempt to reconnect. sleeping (2 * attempt) seconds between each retry, with a max of 60 seconds
	for attempt := 0; ; attempt++ {
		sleepTime := time.Duration(attempt) * 2 * time.Second
		if sleepTime > maxSleepTime {
			time.Sleep(maxSleepTime)
		} else {
			time.Sleep(sleepTime)
		}
		err := c.connect()
		if err == nil {
			break
		}
	}

	go c.readErrors()
	go c.processQueue()
}

// Send queues a notification for sending to the APNs
func (c *Client) Send(n *Notification) {
	// if the channel is full, discard older messages, then queue the new one
	// if we don't discard the older messages, goroutine will get locked trying
	// to put a new notification on
	if len(c.notificationChan) == cap(c.notificationChan) {
		<-c.notificationChan
	}
	c.notificationChan <- n

	gRecents.Lock()
	gRecents.notifications.PushFront(n)
	gRecents.Unlock()

	// we want to keep a cache of the recent messages, in case we get an error
	// from Apple
	if gRecents.notifications.Len() > 200 {
		gRecents.Lock()
		for gRecents.notifications.Len() > 100 {
			gRecents.notifications.Remove(gRecents.notifications.Back())
		}
		gRecents.Unlock()
	}
}

// readErrors sits on the socket waiting for any errors from the APNs. If it
// receives any data or an error, it will close the socket and report any
// relevant errors.
func (c *Client) readErrors() {
	readBuf := make([]byte, 6, 6)
	_, err := c.conn.Read(readBuf)
	// the connection should be closed anytime data is received or an error occurs
	// see apple docs for details
	c.IsConnected = false
	defer c.conn.Close()
	if err != nil {
		log.Printf("Error while reading APNs socket - %v", err)
		return
	}
	var id int32
	binary.Read(bytes.NewReader(readBuf[2:]), binary.BigEndian, &id)

	// if this is an invalid device token error, notify the user
	errID := int(readBuf[1])
	if errID == invalidTokenID {
		gRecents.Lock()
		for e := gRecents.notifications.Front(); e != nil; e = e.Next() {
			n := e.Value.(*Notification)
			if n.identifier == id {
				c.invalidTokenHandler(n.deviceToken)
				break
			}
		}
	} else {
		log.Printf("Error received for notification %d - %v", id, apnsErrors[errID])
	}
}

// processQueue pulls reads Notification objects out of a channel and tries to
// send them to Apple for delivery to a device.
func (c *Client) processQueue() {
	for n := range c.notificationChan {
		if !c.IsConnected {
			// put the notification back in the queue and reconnect this client
			c.notificationChan <- n
			go c.reconnect()
			return // we'll get restarted after the client reconnects
		}

		token, err := hex.DecodeString(n.deviceToken)
		if err != nil {
			log.Printf("Error decoding APNs notification token - %v", err)
			continue
		}

		payload := make(map[string]interface{})
		payload["aps"] = n
		if n.AppData != nil {
			payload["app_data"] = n.AppData
		}
		jsonBytes, err := json.Marshal(payload)
		if err != nil {
			log.Printf("Error marshaling payload to JSON - %v", err)
			continue
		}

		if len(jsonBytes) > MaxPushNotificationBytes {
			log.Printf("Notification is larger than the byte limit (%d). Skipping.", MaxPushNotificationBytes)
			continue
		}

		buf := &bytes.Buffer{}
		binary.Write(buf, binary.BigEndian, uint8(deviceTokenItemID))
		binary.Write(buf, binary.BigEndian, uint16(deviceTokenLength))
		binary.Write(buf, binary.BigEndian, token)
		binary.Write(buf, binary.BigEndian, uint8(payloadItemID))
		binary.Write(buf, binary.BigEndian, uint16(len(jsonBytes)))
		binary.Write(buf, binary.BigEndian, jsonBytes)
		binary.Write(buf, binary.BigEndian, uint8(notificationIdentifierItemID))
		binary.Write(buf, binary.BigEndian, uint16(notificationIdentifierLength))
		binary.Write(buf, binary.BigEndian, n.identifier)
		binary.Write(buf, binary.BigEndian, uint8(expirationDateItemID))
		binary.Write(buf, binary.BigEndian, uint16(expirationDateLength))
		binary.Write(buf, binary.BigEndian, n.expiry)
		binary.Write(buf, binary.BigEndian, uint8(priorityItemID))
		binary.Write(buf, binary.BigEndian, uint16(priorityLength))
		binary.Write(buf, binary.BigEndian, n.priority)

		fullBuf := &bytes.Buffer{}
		binary.Write(fullBuf, binary.BigEndian, uint8(pushCommandValue))
		binary.Write(fullBuf, binary.BigEndian, uint32(buf.Len()))
		binary.Write(fullBuf, binary.BigEndian, buf.Bytes())

		written, err := c.conn.Write(fullBuf.Bytes())
		if err != nil {
			log.Printf("Error writing notification %v to APNs - %v", n, err)
			c.conn.Close()
			c.IsConnected = false
			// requeue the notification and try again later
			c.notificationChan <- n
			go c.reconnect()
			return // we'll get restarted after the client reconnects
		}
		if written != fullBuf.Len() {
			log.Printf("Bytes written didn't equal the bytes in the buffer - w: %d, size: %d",
				written,
				fullBuf.Len())
			c.conn.Close()
			c.IsConnected = false
			c.notificationChan <- n
			go c.reconnect()
			return
		}
	}
}
<commit_msg>Forgot to call Unlock() after Lock()<commit_after>// Copyright 2014 Arash Payan. All rights reserved.
// Use of this source code is governed by the Apache 2
// license that can be found in the LICENSE file.

// Package apns provides a client for using the Apple Push Notification service.
package apns

import (
	"bytes"
	"container/list"
	"crypto/tls"
	"encoding/binary"
	"encoding/hex"
	"encoding/json"
	"log"
	"math/rand"
	"net"
	"sync"
	"time"
)

// ProductionGateway is the host address and port to pass NewClient for
// connecting to the production APNs.
const ProductionGateway = "gateway.push.apple.com:2195"

// SandboxGateway is the host address and port to pass NewClient for
// connecting to the development environment for the APNs.
const SandboxGateway = "gateway.sandbox.push.apple.com:2195"

// MaxPushNotificationBytes is the maximum number of bytes permitted in a payload
// sent to the APNs
const MaxPushNotificationBytes = 2048

// Constants related to the payload fields and their lengths.
const (
	deviceTokenItemID            = 1
	payloadItemID                = 2
	notificationIdentifierItemID = 3
	expirationDateItemID         = 4
	priorityItemID               = 5
	deviceTokenLength            = 32
	notificationIdentifierLength = 4
	expirationDateLength         = 4
	priorityLength               = 1
)

// initialized in init()
var apnsErrors map[int]string

// Push commands always start with command value 2.
const pushCommandValue = 2

// gRecentNotifications stores recently sent notifications in case an error
// is sent by Apple.
var gRecents struct {
	sync.Mutex
	notifications *list.List
}

func init() {
	gRecents.notifications = list.New()
}

// Notification represents a push notification for a specific iOS device
type Notification struct {
	Alert       string      `json:"alert"`
	Badge       int16       `json:"badge"`
	Sound       string      `json:"sound"`
	AppData     interface{} `json:"-"`
	deviceToken string
	identifier  int32
	expiry      uint32
	priority    uint8
}

// APNs error values sent from Apple when a notification has an error.
const (
	processingErrorID    = 1
	missingDeviceTokenID = 2
	missingTopicID       = 3
	missingPayloadID     = 4
	invalidTokenSizeID   = 5
	invalidTopicSizeID   = 6
	invalidPayloadSizeID = 7
	invalidTokenID       = 8
	shutdownID           = 9
	noErrorID            = 10
)

func init() {
	apnsErrors = make(map[int]string)
	apnsErrors[0] = "No errors encountered"
	apnsErrors[processingErrorID] = "Processing error"
	apnsErrors[missingDeviceTokenID] = "Missing device token"
	apnsErrors[missingTopicID] = "Missing topic"
	apnsErrors[missingPayloadID] = "Missing payload"
	apnsErrors[invalidTokenSizeID] = "Invalid token size"
	apnsErrors[invalidTopicSizeID] = "Invalid topic size"
	apnsErrors[invalidPayloadSizeID] = "Invalid payload size"
	apnsErrors[invalidTokenID] = "Invalid token"
	apnsErrors[shutdownID] = "Shutdown"
	apnsErrors[noErrorID] = "None (unknown)"
}

// NewNotification creates an APNs notification that can be delivered to an iOS
// device with the specified devToken.
func NewNotification(devToken string) *Notification {
	n := &Notification{deviceToken: devToken}
	n.identifier = rand.New(rand.NewSource(time.Now().UnixNano())).Int31()
	return n
}

// Client is the broker between an APNs provider and the gateway
type Client struct {
	Gateway             string
	CertificateFile     string
	KeyFile             string
	conn                *tls.Conn
	IsConnected         bool
	notificationChan    chan *Notification
	invalidTokenHandler func(string)
}

// NewClient initializes a Client struct that you can use to send Notifications
// to the APNs. gateway should be either ProductionGateway or SandboxGateway.
// certFile and keyFile are paths to your Apple signed certificate and private
// key. invalidTokenHandler is a function that will be called when an attempt
// send a notification results in an invalid token error from the APNs. When
// you receive these errors, you should remove/disassociate the device token from
// your database/user. This function callback is provided as a simpler alternative
// to implementing a feedback service that would periodically poll Apple for
// invalid tokens.
func NewClient(gateway, certFile, keyFile string, invalidTokenHandler func(string)) *Client {
	c := Client{Gateway: gateway, CertificateFile: certFile, KeyFile: keyFile}
	c.notificationChan = make(chan *Notification, 4096) // 4096 oughtta be enough for anybody
	c.invalidTokenHandler = invalidTokenHandler
	return &c
}

func (c *Client) connect() error {
	c.IsConnected = false
	cert, err := tls.LoadX509KeyPair(c.CertificateFile, c.KeyFile)
	if err != nil {
		return err
	}

	host, _, err := net.SplitHostPort(c.Gateway)
	conf := &tls.Config{
		Certificates: []tls.Certificate{cert},
		ServerName:   host,
	}

	c.conn, err = tls.Dial("tcp", c.Gateway, conf)
	if err != nil {
		return err
	}
	c.IsConnected = true
	return nil
}

// Connect establishes a connection to the APNs gateway specified in NewClient().
// This method blocks until the connection is established.
func (c *Client) Connect() error {
	err := c.connect()
	if err != nil {
		return err
	}

	// start a goroutine to read this connection for any errors messages
	go c.readErrors()

	// start a goroutine to process the queue of notifications
	go c.processQueue()

	return nil
}

func (c *Client) reconnect() {
	log.Print("Attempting to reconnect to APNs")
	maxSleepTime := 60 * time.Second
	// attempt to reconnect. sleeping (2 * attempt) seconds between each retry, with a max of 60 seconds
	for attempt := 0; ; attempt++ {
		sleepTime := time.Duration(attempt) * 2 * time.Second
		if sleepTime > maxSleepTime {
			time.Sleep(maxSleepTime)
		} else {
			time.Sleep(sleepTime)
		}
		err := c.connect()
		if err == nil {
			break
		}
	}

	go c.readErrors()
	go c.processQueue()
}

// Send queues a notification for sending to the APNs
func (c *Client) Send(n *Notification) {
	// if the channel is full, discard older messages, then queue the new one
	// if we don't discard the older messages, goroutine will get locked trying
	// to put a new notification on
	if len(c.notificationChan) == cap(c.notificationChan) {
		<-c.notificationChan
	}
	c.notificationChan <- n

	gRecents.Lock()
	gRecents.notifications.PushFront(n)
	gRecents.Unlock()

	// we want to keep a cache of the recent messages, in case we get an error
	// from Apple
	if gRecents.notifications.Len() > 200 {
		gRecents.Lock()
		for gRecents.notifications.Len() > 100 {
			gRecents.notifications.Remove(gRecents.notifications.Back())
		}
		gRecents.Unlock()
	}
}

// readErrors sits on the socket waiting for any errors from the APNs. If it
// receives any data or an error, it will close the socket and report any
// relevant errors.
func (c *Client) readErrors() {
	readBuf := make([]byte, 6, 6)
	_, err := c.conn.Read(readBuf)
	// the connection should be closed anytime data is received or an error occurs
	// see apple docs for details
	c.IsConnected = false
	defer c.conn.Close()
	if err != nil {
		log.Printf("Error while reading APNs socket - %v", err)
		return
	}
	var id int32
	binary.Read(bytes.NewReader(readBuf[2:]), binary.BigEndian, &id)

	// if this is an invalid device token error, notify the user
	errID := int(readBuf[1])
	if errID == invalidTokenID {
		gRecents.Lock()
		for e := gRecents.notifications.Front(); e != nil; e = e.Next() {
			n := e.Value.(*Notification)
			if n.identifier == id {
				c.invalidTokenHandler(n.deviceToken)
				break
			}
		}
		gRecents.Unlock()
	} else {
		log.Printf("Error received for notification %d - %v", id, apnsErrors[errID])
	}
}

// processQueue pulls reads Notification objects out of a channel and tries to
// send them to Apple for delivery to a device.
func (c *Client) processQueue() {
	for n := range c.notificationChan {
		if !c.IsConnected {
			// put the notification back in the queue and reconnect this client
			c.notificationChan <- n
			go c.reconnect()
			return // we'll get restarted after the client reconnects
		}

		token, err := hex.DecodeString(n.deviceToken)
		if err != nil {
			log.Printf("Error decoding APNs notification token - %v", err)
			continue
		}

		payload := make(map[string]interface{})
		payload["aps"] = n
		if n.AppData != nil {
			payload["app_data"] = n.AppData
		}
		jsonBytes, err := json.Marshal(payload)
		if err != nil {
			log.Printf("Error marshaling payload to JSON - %v", err)
			continue
		}

		if len(jsonBytes) > MaxPushNotificationBytes {
			log.Printf("Notification is larger than the byte limit (%d). Skipping.", MaxPushNotificationBytes)
			continue
		}

		buf := &bytes.Buffer{}
		binary.Write(buf, binary.BigEndian, uint8(deviceTokenItemID))
		binary.Write(buf, binary.BigEndian, uint16(deviceTokenLength))
		binary.Write(buf, binary.BigEndian, token)
		binary.Write(buf, binary.BigEndian, uint8(payloadItemID))
		binary.Write(buf, binary.BigEndian, uint16(len(jsonBytes)))
		binary.Write(buf, binary.BigEndian, jsonBytes)
		binary.Write(buf, binary.BigEndian, uint8(notificationIdentifierItemID))
		binary.Write(buf, binary.BigEndian, uint16(notificationIdentifierLength))
		binary.Write(buf, binary.BigEndian, n.identifier)
		binary.Write(buf, binary.BigEndian, uint8(expirationDateItemID))
		binary.Write(buf, binary.BigEndian, uint16(expirationDateLength))
		binary.Write(buf, binary.BigEndian, n.expiry)
		binary.Write(buf, binary.BigEndian, uint8(priorityItemID))
		binary.Write(buf, binary.BigEndian, uint16(priorityLength))
		binary.Write(buf, binary.BigEndian, n.priority)

		fullBuf := &bytes.Buffer{}
		binary.Write(fullBuf, binary.BigEndian, uint8(pushCommandValue))
		binary.Write(fullBuf, binary.BigEndian, uint32(buf.Len()))
		binary.Write(fullBuf, binary.BigEndian, buf.Bytes())

		written, err := c.conn.Write(fullBuf.Bytes())
		if err != nil {
			log.Printf("Error writing notification %v to APNs - %v", n, err)
			c.conn.Close()
			c.IsConnected = false
			// requeue the notification and try again later
			c.notificationChan <- n
			go c.reconnect()
			return // we'll get restarted after the client reconnects
		}
		if written != fullBuf.Len() {
			log.Printf("Bytes written didn't equal the bytes in the buffer - w: %d, size: %d",
				written,
				fullBuf.Len())
			c.conn.Close()
			c.IsConnected = false
			c.notificationChan <- n
			go c.reconnect()
			return
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"os"
	//"encoding/json"
	"fmt"
	"strings"
	//"io/ioutil"
	"path/filepath"
)

var cmdAura = &Command{
	Usage: "aura",
	Short: "force aura push -fileName=<filepath>",
	Long: `
	The aura command needs context to work. If you execute "aura get"
	it will create a folder structure that provides the context for
	aura components on disk.

	The aura components will be created in "metadata/aurabundles/<componentname>"
	relative to the current working directory and a .manifest file will be
	created that associates components and their artifacts with their ids in
	the database.

	To create a new component (application, evt or component), create a new
	folder under "aurabundles". Then create a new file in your new folder. You
	must follow a naming convention for your files to enable proper definition
	of the component type.

	Naming convention <compnentName><artifact type>.<file type extension>
	Examples: 	metadata
					aurabundles
						MyApp
							MyAppApplication.app
							MyAppStyle.css
						MyList
							MyComponent.cmp
							MyComponentHelper.js
							MyComponentStyle.css

	force aura push -f <fullFilePath> -b <bundle name>

	force aura create -t=<entity type> <entityName>

	force aura delete -f=<fullFilePath>

	force aura list

	`,
}

func init() {
	cmdAura.Run = runAura
	cmdAura.Flag.StringVar(fileName, "f", "", "fully qualified file name for entity")
	cmdAura.Flag.StringVar(auraentitytype, "entitytype", "", "fully qualified file name for entity")
	cmdAura.Flag.StringVar(auraentityname, "entityname", "", "fully qualified file name for entity")
}

var (
	auraentitytype = cmdAura.Flag.String("t", "", "aura entity type")
	auraentityname = cmdAura.Flag.String("n", "", "aura entity name")
)

func runAura(cmd *Command, args []string) {
	if err := cmd.Flag.Parse(args[1:]); err != nil {
		os.Exit(2)
	}
	force, _ := ActiveForce()

	subcommand := args[0]

	switch strings.ToLower(subcommand) {
	case "create":
		if *auraentitytype == "" || *auraentityname == "" {
			fmt.Println("Must specify entity type and name")
			os.Exit(2)
		}

	case "delete":
		runDeleteAura()
	case "list":
		bundles, err := force.GetAuraBundlesList()
		if err != nil {
			ErrorAndExit("Ooops")
		}
		for _, bundle := range bundles.Records {
			fmt.Println(bundle["DeveloperName"])
		}
	case "push":
		runPushAura(cmd, args)
	}
}

func runDeleteAura() {
	absPath, _ := filepath.Abs(*fileName)
	*fileName = absPath

	if InAuraBundlesFolder(*fileName) {
		fmt.Println("Yup, in aura folder")
		info, err := os.Stat(*fileName)
		if err != nil {
			ErrorAndExit(err.Error())
		}
		manifest, err := GetManifest(*fileName)
		isBundle := false
		if info.IsDir() {
			manifest, err = GetManifest(filepath.Join(*fileName, ".manifest"))
			if err != nil {
				// Try to look up the bundle by name
				force, _ := ActiveForce()
				b, err := force.GetAuraBundleByName(filepath.Base(*fileName))
				if err != nil {
					fmt.Println(err.Error())
				} else {
					if len(b.Records) == 0 {
						ErrorAndExit(fmt.Sprintf("No bundle definition named %q", filepath.Base(*fileName)))
					} else {
						bid := b.Records[0]["Id"].(string)
						err = force.DeleteToolingRecord("AuraDefinitionBundle", bid)
						if err != nil {
							ErrorAndExit(err.Error())
						}
						// Now walk the bundle and remove all the atrifacts
						filepath.Walk(*fileName, func(path string, inf os.FileInfo, err error) error {
							os.Remove(path)
							return nil
						})
						os.Remove(*fileName)
						fmt.Println("Bundle ", filepath.Base(*fileName), " deleted.")
						return;
					}
				}
			}
			isBundle = true
		}

		for key := range manifest.Files {
			mfile := manifest.Files[key].FileName
			cfile := *fileName
			if !filepath.IsAbs(mfile) {
				cfile = filepath.Base(cfile)
			}
			if isBundle {
				if !filepath.IsAbs(mfile) {
					cfile = filepath.Join(*fileName, mfile)
				} else {
					cfile = mfile
					deleteAuraDefinition(manifest, key)
				}
			} else {
				if mfile == cfile {
					fmt.Println("Found the manifest entry: ", manifest.Files[key].ComponentId)
					deleteAuraDefinition(manifest, key)
					return
				}
			}
		}
		if isBundle {
			// Need to remove the bundle using the id in the manifest
			deleteAuraDefinitionBundle(manifest)
		}
	}
}
func deleteAuraDefinitionBundle(manifest BundleManifest) {
	force, err := ActiveForce()
	err = force.DeleteToolingRecord("AuraDefinitionBundle", manifest.Id)
	if err != nil {
		ErrorAndExit(err.Error())
	}
	os.Remove(filepath.Join(*fileName, ".manifest"))
	os.Remove(*fileName)
}

func deleteAuraDefinition(manifest BundleManifest, key int) {
	force, err := ActiveForce()
	err = force.DeleteToolingRecord("AuraDefinition", manifest.Files[key].ComponentId)
	if err != nil {
		ErrorAndExit(err.Error())
	}
	manifest.Files = append(manifest.Files[:key], manifest.Files[key+1:]...)
	os.Remove(*fileName)
}
<commit_msg>sublime support<commit_after>package main

import (
	"os"
	"encoding/json"
	"fmt"
	"strings"
	"io/ioutil"
	"path/filepath"
)

var cmdAura = &Command{
	Usage: "aura",
	Short: "force aura push -fileName=<filepath>",
	Long: `
	The aura command needs context to work. If you execute "aura get"
	it will create a folder structure that provides the context for
	aura components on disk.

	The aura components will be created in "metadata/aurabundles/<componentname>"
	relative to the current working directory and a .manifest file will be
	created that associates components and their artifacts with their ids in
	the database.

	To create a new component (application, evt or component), create a new
	folder under "aurabundles". Then create a new file in your new folder. You
	must follow a naming convention for your files to enable proper definition
	of the component type.

	Naming convention <compnentName><artifact type>.<file type extension>
	Examples: 	metadata
					aurabundles
						MyApp
							MyAppApplication.app
							MyAppStyle.css
						MyList
							MyComponent.cmp
							MyComponentHelper.js
							MyComponentStyle.css

	force aura push -f <fullFilePath> -b <bundle name>

	force aura create -t=<entity type> <entityName>

	force aura delete -f=<fullFilePath>

	force aura list

	`,
}

func init() {
	cmdAura.Run = runAura
	cmdAura.Flag.StringVar(fileName, "f", "", "fully qualified file name for entity")
	cmdAura.Flag.StringVar(auraentitytype, "entitytype", "", "fully qualified file name for entity")
	cmdAura.Flag.StringVar(auraentityname, "entityname", "", "fully qualified file name for entity")
}

var (
	auraentitytype = cmdAura.Flag.String("t", "", "aura entity type")
	auraentityname = cmdAura.Flag.String("n", "", "aura entity name")
)

func runAura(cmd *Command, args []string) {
	if err := cmd.Flag.Parse(args[0:]); err != nil {
		os.Exit(2)
	}

	force, _ := ActiveForce()

	subcommand := args[0]
	// Sublime hack - the way sublime passes parameters seems to
	// break the flag parsing by sending a single element array
	// for the args. ARGH!!!
	if strings.HasPrefix(subcommand, "delete ") {
		what := strings.Split(subcommand, " ")
		if err := cmd.Flag.Parse(what[1:]); err != nil {
			ErrorAndExit(err.Error())
		}
		subcommand = what[0]
	}

	switch strings.ToLower(subcommand) {
	case "create":
		/*if *auraentitytype == "" || *auraentityname == "" {
			fmt.Println("Must specify entity type and name")
			os.Exit(2)
		}*/
		ErrorAndExit("force aura create not yet implemented...")

	case "delete":
		runDeleteAura()
	case "list":
		bundles, err := force.GetAuraBundlesList()
		if err != nil {
			ErrorAndExit("Ooops")
		}
		for _, bundle := range bundles.Records {
			fmt.Println(bundle["DeveloperName"])
		}
	case "push":
		runPushAura(cmd, args)
	}
}

func runDeleteAura() {
	absPath, _ := filepath.Abs(*fileName)
	*fileName = absPath

	if InAuraBundlesFolder(*fileName) {
		info, err := os.Stat(*fileName)
		if err != nil {
			ErrorAndExit(err.Error())
		}
		manifest, err := GetManifest(*fileName)
		isBundle := false
		if info.IsDir() {
			manifest, err = GetManifest(filepath.Join(*fileName, ".manifest"))
			if err != nil {
				// Try to look up the bundle by name
				force, _ := ActiveForce()
				b, err := force.GetAuraBundleByName(filepath.Base(*fileName))
				if err != nil {
					fmt.Println(err.Error())
				} else {
					if len(b.Records) == 0 {
						ErrorAndExit(fmt.Sprintf("No bundle definition named %q", filepath.Base(*fileName)))
					} else {
						bid := b.Records[0]["Id"].(string)
						err = force.DeleteToolingRecord("AuraDefinitionBundle", bid)
						if err != nil {
							ErrorAndExit(err.Error())
						}
						// Now walk the bundle and remove all the atrifacts
						filepath.Walk(*fileName, func(path string, inf os.FileInfo, err error) error {
							os.Remove(path)
							return nil
						})
						os.Remove(*fileName)
						fmt.Println("Bundle ", filepath.Base(*fileName), " deleted.")
						return;
					}
				}
			}
			isBundle = true
		}

		for key := range manifest.Files {
			mfile := manifest.Files[key].FileName
			cfile := *fileName
			if !filepath.IsAbs(mfile) {
				cfile = filepath.Base(cfile)
			}
			if isBundle {
				if !filepath.IsAbs(mfile) {
					cfile = filepath.Join(*fileName, mfile)
				} else {
					cfile = mfile
					deleteAuraDefinition(manifest, key)
				}
			} else {
				if mfile == cfile {
					deleteAuraDefinition(manifest, key)
					return
				}
			}
		}
		if isBundle {
			// Need to remove the bundle using the id in the manifest
			deleteAuraDefinitionBundle(manifest)
		}
	}
}
func deleteAuraDefinitionBundle(manifest BundleManifest) {
	force, err := ActiveForce()
	err = force.DeleteToolingRecord("AuraDefinitionBundle", manifest.Id)
	if err != nil {
		ErrorAndExit(err.Error())
	}
	os.Remove(filepath.Join(*fileName, ".manifest"))
	os.Remove(*fileName)
}

func deleteAuraDefinition(manifest BundleManifest, key int) {
	force, err := ActiveForce()
	err = force.DeleteToolingRecord("AuraDefinition", manifest.Files[key].ComponentId)
	if err != nil {
		ErrorAndExit(err.Error())
	}
	fname := manifest.Files[key].FileName
	os.Remove(fname)
	manifest.Files = append(manifest.Files[:key], manifest.Files[key+1:]...)
	bmBody, _ := json.Marshal(manifest)
	ioutil.WriteFile(filepath.Join(filepath.Dir(fname), ".manifest"), bmBody, 0644)
}
<|endoftext|>


<commit_before>// Package goauth implements cookie/session based authentication. Intended for
// use with the net/http or github.com/gorilla/mux packages, but may work with
// github.com/codegangsta/martini as well. Internally, credentials are stored
// as a username + password hash, computed with bcrypt.
//
// Users can be redirected to the page that triggered an authentication error.
//
// Messages describing the reason a user could not authenticate are saved in a
// cookie, and can be accessed with the goauth.Messages function.
package goauth

import (
    "errors"
    "net/http"
    "code.google.com/p/go.crypto/bcrypt"
    "github.com/gorilla/sessions"
    "github.com/gorilla/context"
)

// UserData represents a single user. It contains the users username and email
// as well as a has of their username and password.
type UserData struct {
    Username string
    Email string
    Hash []byte
}

// Authorizer structures contain the store of user session cookies a reference
// to a backend storage system.
type Authorizer struct {
    cookiejar *sessions.CookieStore
    backend AuthBackend
}

// The AuthBackend interface defines a set of methods an AuthBackend must
// implement.
type AuthBackend interface {
    SaveUser(u UserData) error
    User(username string) (user UserData, ok bool)
    Users() (users []UserData)
    DeleteUser(username string) error
}

// Helper function to add a user directed message to a message queue.
func (a Authorizer) addMessage(rw http.ResponseWriter, req *http.Request, message string) {
    messageSession, _ := a.cookiejar.Get(req, "messages")
    defer messageSession.Save(req, rw)
    messageSession.AddFlash(message)
}

// Helper function to save a redirect to the page a user tried to visit before
// logging in.
func (a Authorizer) goBack(rw http.ResponseWriter, req *http.Request) {
    redirectSession, _ := a.cookiejar.Get(req, "redirects");
    defer redirectSession.Save(req, rw)
    redirectSession.Flashes()
    redirectSession.AddFlash(req.URL.Path)
}

// NewAuthorizer returns a new Authorizer given an AuthBackend and a cookie
// store key.  If the key changes, logged in users will need to reauthenticate.
func NewAuthorizer(backend AuthBackend, key []byte) (a Authorizer) {
    a.cookiejar = sessions.NewCookieStore([]byte(key))
    a.backend = backend
    return a
}

// Login logs a user in. They will be redirected to dest or to the last
// location an authorization redirect was triggered (if found) on success. A
// message will be added to the session on failure with the reason.
func (a Authorizer) Login(rw http.ResponseWriter, req *http.Request, u string, p string, dest string) error {
    session, _ := a.cookiejar.Get(req, "auth")
    if session.Values["username"] != nil {
        return errors.New("already authenticated")
    }
    if user, ok := a.backend.User(u); ok {
        verify := bcrypt.CompareHashAndPassword(user.Hash, []byte(u + p))
        if verify != nil {
            a.addMessage(rw, req, "Invalid username or password.")
            return errors.New("password doesn't match")
        }
    } else {
        a.addMessage(rw, req, "Invalid username or password.")
        return errors.New("user not found")
    }
    session.Values["username"] = u
    session.Save(req, rw)

    redirectSession, _ := a.cookiejar.Get(req, "redirects")
    if flashes := redirectSession.Flashes(); len(flashes) > 0 {
        dest = flashes[0].(string)
    }
    http.Redirect(rw, req, dest, http.StatusSeeOther)
    return nil
}

// Register and save a new user. Returns an error and adds a message if the
// username is in use.
func (a Authorizer) Register(rw http.ResponseWriter, req *http.Request, u string, p string, e string) error {
    if _, ok := a.backend.User(u); ok {
        a.addMessage(rw, req, "Username has been taken.")
        return errors.New("user already exists")
    }

    hash, err := bcrypt.GenerateFromPassword([]byte(u + p), 8)
    if err != nil {
        return errors.New("couldn't save password: " + err.Error())
    }

    user := UserData{u, e, hash}

    err = a.backend.SaveUser(user)
    if err != nil {
        a.addMessage(rw, req, err.Error())
    }
    return nil
}

// Authorize checks if a user is logged in and returns an error on failed
// authentication. If redirectWithMessage is set, the page being authorized
// will be saved and a "Login to do that." message will be saved to the
// messages list. The next time the user logs in, they will be redirected back
// to the saved page.
func (a Authorizer) Authorize(rw http.ResponseWriter, req *http.Request, redirectWithMessage bool) error {
    authSession, err := a.cookiejar.Get(req, "auth")
    if err != nil {
        if redirectWithMessage {
            a.goBack(rw, req)
        }
        return errors.New("new authorization session. Possible restart of server")
    }
    if authSession.IsNew {
        if redirectWithMessage {
            a.goBack(rw, req)
            a.addMessage(rw, req, "Log in to do that.")
        }
        return errors.New("no session existed")
    }
    username := authSession.Values["username"]
    if !authSession.IsNew && username != nil {
        if _, ok := a.backend.User(username.(string)); !ok {
            authSession.Options.MaxAge = -1 // kill the cookie
            authSession.Save(req, rw)
            if redirectWithMessage {
                a.goBack(rw, req)
                a.addMessage(rw, req, "Log in to do that.")
            }
            return errors.New("user not found")
        }
    }
    if username == nil {
        if redirectWithMessage {
            a.goBack(rw, req)
            a.addMessage(rw, req, "Log in to do that.")
        }
        return errors.New("user not logged in")
    }
    context.Set(req, "username", username)
    return nil
}

// Logout clears an authentication session and add a logged out message.
func (a Authorizer) Logout(rw http.ResponseWriter, req *http.Request) error {
    session, _ := a.cookiejar.Get(req, "auth")
    defer session.Save(req, rw)

    session.Options.MaxAge = -1 // kill the cookie
    a.addMessage(rw, req, "Logged out.")
    return nil
}

func (a Authorizer) DeleteUser(username string) error {
    err := a.backend.DeleteUser(username)
    return err
}

// Messages fetches a list of saved messages. Use this to get a nice message to print to
// the user on a login page or registration page in case something happened
// (username taken, invalid credentials, successful logout, etc).
func (a Authorizer) Messages(rw http.ResponseWriter, req *http.Request) []string {
    session, _ := a.cookiejar.Get(req, "messages")
    flashes := session.Flashes()
    session.Save(req, rw)
    var messages []string
    for _, val := range flashes {
        messages = append(messages, val.(string))
    }
    return messages
}
<commit_msg>Docs for deleteuser<commit_after>// Package goauth implements cookie/session based authentication. Intended for
// use with the net/http or github.com/gorilla/mux packages, but may work with
// github.com/codegangsta/martini as well. Internally, credentials are stored
// as a username + password hash, computed with bcrypt.
//
// Users can be redirected to the page that triggered an authentication error.
//
// Messages describing the reason a user could not authenticate are saved in a
// cookie, and can be accessed with the goauth.Messages function.
package goauth

import (
    "errors"
    "net/http"
    "code.google.com/p/go.crypto/bcrypt"
    "github.com/gorilla/sessions"
    "github.com/gorilla/context"
)

// UserData represents a single user. It contains the users username and email
// as well as a has of their username and password.
type UserData struct {
    Username string
    Email string
    Hash []byte
}

// Authorizer structures contain the store of user session cookies a reference
// to a backend storage system.
type Authorizer struct {
    cookiejar *sessions.CookieStore
    backend AuthBackend
}

// The AuthBackend interface defines a set of methods an AuthBackend must
// implement.
type AuthBackend interface {
    SaveUser(u UserData) error
    User(username string) (user UserData, ok bool)
    Users() (users []UserData)
    DeleteUser(username string) error
}

// Helper function to add a user directed message to a message queue.
func (a Authorizer) addMessage(rw http.ResponseWriter, req *http.Request, message string) {
    messageSession, _ := a.cookiejar.Get(req, "messages")
    defer messageSession.Save(req, rw)
    messageSession.AddFlash(message)
}

// Helper function to save a redirect to the page a user tried to visit before
// logging in.
func (a Authorizer) goBack(rw http.ResponseWriter, req *http.Request) {
    redirectSession, _ := a.cookiejar.Get(req, "redirects");
    defer redirectSession.Save(req, rw)
    redirectSession.Flashes()
    redirectSession.AddFlash(req.URL.Path)
}

// NewAuthorizer returns a new Authorizer given an AuthBackend and a cookie
// store key.  If the key changes, logged in users will need to reauthenticate.
func NewAuthorizer(backend AuthBackend, key []byte) (a Authorizer) {
    a.cookiejar = sessions.NewCookieStore([]byte(key))
    a.backend = backend
    return a
}

// Login logs a user in. They will be redirected to dest or to the last
// location an authorization redirect was triggered (if found) on success. A
// message will be added to the session on failure with the reason.
func (a Authorizer) Login(rw http.ResponseWriter, req *http.Request, u string, p string, dest string) error {
    session, _ := a.cookiejar.Get(req, "auth")
    if session.Values["username"] != nil {
        return errors.New("already authenticated")
    }
    if user, ok := a.backend.User(u); ok {
        verify := bcrypt.CompareHashAndPassword(user.Hash, []byte(u + p))
        if verify != nil {
            a.addMessage(rw, req, "Invalid username or password.")
            return errors.New("password doesn't match")
        }
    } else {
        a.addMessage(rw, req, "Invalid username or password.")
        return errors.New("user not found")
    }
    session.Values["username"] = u
    session.Save(req, rw)

    redirectSession, _ := a.cookiejar.Get(req, "redirects")
    if flashes := redirectSession.Flashes(); len(flashes) > 0 {
        dest = flashes[0].(string)
    }
    http.Redirect(rw, req, dest, http.StatusSeeOther)
    return nil
}

// Register and save a new user. Returns an error and adds a message if the
// username is in use.
func (a Authorizer) Register(rw http.ResponseWriter, req *http.Request, u string, p string, e string) error {
    if _, ok := a.backend.User(u); ok {
        a.addMessage(rw, req, "Username has been taken.")
        return errors.New("user already exists")
    }

    hash, err := bcrypt.GenerateFromPassword([]byte(u + p), 8)
    if err != nil {
        return errors.New("couldn't save password: " + err.Error())
    }

    user := UserData{u, e, hash}

    err = a.backend.SaveUser(user)
    if err != nil {
        a.addMessage(rw, req, err.Error())
    }
    return nil
}

// Authorize checks if a user is logged in and returns an error on failed
// authentication. If redirectWithMessage is set, the page being authorized
// will be saved and a "Login to do that." message will be saved to the
// messages list. The next time the user logs in, they will be redirected back
// to the saved page.
func (a Authorizer) Authorize(rw http.ResponseWriter, req *http.Request, redirectWithMessage bool) error {
    authSession, err := a.cookiejar.Get(req, "auth")
    if err != nil {
        if redirectWithMessage {
            a.goBack(rw, req)
        }
        return errors.New("new authorization session. Possible restart of server")
    }
    if authSession.IsNew {
        if redirectWithMessage {
            a.goBack(rw, req)
            a.addMessage(rw, req, "Log in to do that.")
        }
        return errors.New("no session existed")
    }
    username := authSession.Values["username"]
    if !authSession.IsNew && username != nil {
        if _, ok := a.backend.User(username.(string)); !ok {
            authSession.Options.MaxAge = -1 // kill the cookie
            authSession.Save(req, rw)
            if redirectWithMessage {
                a.goBack(rw, req)
                a.addMessage(rw, req, "Log in to do that.")
            }
            return errors.New("user not found")
        }
    }
    if username == nil {
        if redirectWithMessage {
            a.goBack(rw, req)
            a.addMessage(rw, req, "Log in to do that.")
        }
        return errors.New("user not logged in")
    }
    context.Set(req, "username", username)
    return nil
}

// Logout clears an authentication session and add a logged out message.
func (a Authorizer) Logout(rw http.ResponseWriter, req *http.Request) error {
    session, _ := a.cookiejar.Get(req, "auth")
    defer session.Save(req, rw)

    session.Options.MaxAge = -1 // kill the cookie
    a.addMessage(rw, req, "Logged out.")
    return nil
}

// DeleteUser removes a user from the Authorizer.
func (a Authorizer) DeleteUser(username string) error {
    err := a.backend.DeleteUser(username)
    return err
}

// Messages fetches a list of saved messages. Use this to get a nice message to print to
// the user on a login page or registration page in case something happened
// (username taken, invalid credentials, successful logout, etc).
func (a Authorizer) Messages(rw http.ResponseWriter, req *http.Request) []string {
    session, _ := a.cookiejar.Get(req, "messages")
    flashes := session.Flashes()
    session.Save(req, rw)
    var messages []string
    for _, val := range flashes {
        messages = append(messages, val.(string))
    }
    return messages
}
<|endoftext|>


<commit_before>package bale

type Logger interface {
	logPrinter
	logFormatter
}

// Log functions that handle arguments in the manner of fmt.Print.
type logPrinter interface {
	// Info logs to the INFO log.
	Info(args ...interface{})

	// Warning logs to the WARNING and INFO logs.
	Warn(args ...interface{})

	// Error logs to the ERROR, WARNING, and INFO logs.
	Error(args ...interface{})

	// Fatal logs to the FATAL, ERROR, WARNING, and INFO logs, and is expected
	// to exit the program.
	Fatal(args ...interface{})
}

// Log functions that handle arguments in the manner of fmt.Printf.
type logFormatter interface {
	// Infof logs to the INFO log.
	Infof(format string, args ...interface{})

	// Warningf logs to the WARNING and INFO logs.
	Warnf(format string, args ...interface{})

	// Errorf logs to the ERROR, WARNING, and INFO logs.
	Errorf(format string, args ...interface{})

	// Fatalf logs to the FATAL, ERROR, WARNING, and INFO logs, and is expected
	// to exit the program.
	Fatalf(format string, args ...interface{})
}

// Log functions that handle arguments in the manner of fmt.Println.
type Logliner interface {
	// Infoln logs to the INFO log.
	Infoln(args ...interface{})

	// Warningln logs to the WARNING and INFO logs.
	Warnln(args ...interface{})

	// Errorln logs to the ERROR, WARNING, and INFO logs.
	Errorln(args ...interface{})

	// Fatalln logs to the FATAL, ERROR, WARNING, and INFO logs, and is expected
	// to exit the program.
	Fatalln(args ...interface{})
}
<commit_msg>Update comments/documentation.<commit_after>// Package bale provides wrappers around go logging libraries, allowing users of the
// library to easily swap between logging implementations without changing client code.
package bale

// Logger defines the minimum set of methods implemented by all logging implementations.
type Logger interface {
	logPrinter
	logFormatter
}

// Log functions that handle arguments like fmt.Print.
type logPrinter interface {
	// Info logs to the INFO log.
	Info(args ...interface{})

	// Warning logs to the WARNING and INFO logs.
	Warn(args ...interface{})

	// Error logs to the ERROR, WARNING, and INFO logs.
	Error(args ...interface{})

	// Fatal logs to the FATAL, ERROR, WARNING, and INFO logs, and is expected
	// to exit the program.
	Fatal(args ...interface{})
}

// Log functions that handle arguments like fmt.Printf.
type logFormatter interface {
	// Infof logs to the INFO log.
	Infof(format string, args ...interface{})

	// Warningf logs to the WARNING and INFO logs.
	Warnf(format string, args ...interface{})

	// Errorf logs to the ERROR, WARNING, and INFO logs.
	Errorf(format string, args ...interface{})

	// Fatalf logs to the FATAL, ERROR, WARNING, and INFO logs, and is expected
	// to exit the program.
	Fatalf(format string, args ...interface{})
}

// Log functions that handle arguments like fmt.Println.
type Logliner interface {
	// Infoln logs to the INFO log.
	Infoln(args ...interface{})

	// Warningln logs to the WARNING and INFO logs.
	Warnln(args ...interface{})

	// Errorln logs to the ERROR, WARNING, and INFO logs.
	Errorln(args ...interface{})

	// Fatalln logs to the FATAL, ERROR, WARNING, and INFO logs, and is expected
	// to exit the program.
	Fatalln(args ...interface{})
}
<|endoftext|>


<commit_before>package bort

import (
	"bytes"
	"fmt"
	"log"
	"net/rpc"
	"sort"
	"strings"
	"text/tabwriter"

	"github.com/thoj/go-ircevent"
)

const (
	DefaultPort = 1234
	cmdPrefix   = "!"
)

var (
	handlers = map[string]*Handler{}
	event    = &Event{}
	help     string
)

type ResponseType int

const (
	None ResponseType = iota
	Action
	PrivMsg
)

type Event struct{}

func (e *Event) Process(msg *Message, res *Response) error {
	res.Type = PrivMsg
	res.Target = msg.Target
	if msg.Command == "help" {
		res.Type = PrivMsg
		res.Target = msg.Nick
		res.Text = help
		return nil
	}
	if handler, ok := handlers[msg.Command]; ok {
		return handler.handle(msg, res)
	}
	return nil
}

type Message struct {
	Channel string
	Command string
	Host    string
	Nick    string
	Raw     string
	Source  string
	Target  string
	Text    string
	User    string
}

func NewMessage(channel string, evt *irc.Event) *Message {
	msg := &Message{
		Channel: channel,
		Host:    evt.Host,
		Nick:    evt.Nick,
		Raw:     evt.Raw,
		Source:  evt.Source,
		Target:  channel,
		Command: "*",
		Text:    evt.Message(),
		User:    evt.User,
	}

	text := strings.TrimSpace(evt.Message())
	if strings.HasPrefix(text, cmdPrefix) {
		cmdStr := text[1:] + " " // append space to ensure SplitN returns 2 strings
		cmdAndArgs := strings.SplitN(cmdStr, " ", 2)
		if len(cmdAndArgs) == 2 {
			msg.Command = cmdAndArgs[0]
			msg.Text = strings.TrimSpace(cmdAndArgs[1])
		}
	}
	if evt.Arguments[0] != channel {
		msg.Target = evt.Nick
	}
	return msg
}

type Response struct {
	Type   ResponseType
	Target string
	Text   string
}

func NewResponse() *Response {
	return &Response{}
}

type HandleFunc func(msg *Message, res *Response) error

type Handler struct {
	handle HandleFunc
	help   string
}

func Register(cmd, help string, handle HandleFunc) error {
	if _, ok := handlers[cmd]; ok {
		return fmt.Errorf("%s: handler already registered", cmd)
	}
	handlers[cmd] = &Handler{help: help, handle: handle}
	return nil
}

func Init() {
	buf := &bytes.Buffer{}
	tabWrite := tabwriter.NewWriter(buf, 2, 0, 1, ' ', 0)
	cmds := sort.StringSlice{}
	for cmd := range handlers {
		cmds = append(cmds, cmd)
	}
	cmds.Sort()
	for _, cmd := range cmds {
		fmt.Fprintf(tabWrite, "%s:\t%s\n", cmd, handlers[cmd].help)
	}
	tabWrite.Flush()
	help = buf.String()
}

func init() {
	err := rpc.Register(event)
	if err != nil {
		log.Fatal(err)
	}
}
<commit_msg>Removed redundant assignment.<commit_after>package bort

import (
	"bytes"
	"fmt"
	"log"
	"net/rpc"
	"sort"
	"strings"
	"text/tabwriter"

	"github.com/thoj/go-ircevent"
)

const (
	DefaultPort = 1234
	cmdPrefix   = "!"
)

var (
	handlers = map[string]*Handler{}
	event    = &Event{}
	help     string
)

type ResponseType int

const (
	None ResponseType = iota
	Action
	PrivMsg
)

type Event struct{}

func (e *Event) Process(msg *Message, res *Response) error {
	res.Type = PrivMsg
	res.Target = msg.Target
	if msg.Command == "help" {
		res.Target = msg.Nick
		res.Text = help
		return nil
	}
	if handler, ok := handlers[msg.Command]; ok {
		return handler.handle(msg, res)
	}
	return nil
}

type Message struct {
	Channel string
	Command string
	Host    string
	Nick    string
	Raw     string
	Source  string
	Target  string
	Text    string
	User    string
}

func NewMessage(channel string, evt *irc.Event) *Message {
	msg := &Message{
		Channel: channel,
		Host:    evt.Host,
		Nick:    evt.Nick,
		Raw:     evt.Raw,
		Source:  evt.Source,
		Target:  channel,
		Command: "*",
		Text:    evt.Message(),
		User:    evt.User,
	}

	text := strings.TrimSpace(evt.Message())
	if strings.HasPrefix(text, cmdPrefix) {
		cmdStr := text[1:] + " " // append space to ensure SplitN returns 2 strings
		cmdAndArgs := strings.SplitN(cmdStr, " ", 2)
		if len(cmdAndArgs) == 2 {
			msg.Command = cmdAndArgs[0]
			msg.Text = strings.TrimSpace(cmdAndArgs[1])
		}
	}
	if evt.Arguments[0] != channel {
		msg.Target = evt.Nick
	}
	return msg
}

type Response struct {
	Type   ResponseType
	Target string
	Text   string
}

func NewResponse() *Response {
	return &Response{}
}

type HandleFunc func(msg *Message, res *Response) error

type Handler struct {
	handle HandleFunc
	help   string
}

func Register(cmd, help string, handle HandleFunc) error {
	if _, ok := handlers[cmd]; ok {
		return fmt.Errorf("%s: handler already registered", cmd)
	}
	handlers[cmd] = &Handler{help: help, handle: handle}
	return nil
}

func Init() {
	buf := &bytes.Buffer{}
	tabWrite := tabwriter.NewWriter(buf, 2, 0, 1, ' ', 0)
	cmds := sort.StringSlice{}
	for cmd := range handlers {
		cmds = append(cmds, cmd)
	}
	cmds.Sort()
	for _, cmd := range cmds {
		fmt.Fprintf(tabWrite, "%s:\t%s\n", cmd, handlers[cmd].help)
	}
	tabWrite.Flush()
	help = buf.String()
}

func init() {
	err := rpc.Register(event)
	if err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/rand"
	"fmt"
	"strings"
	"sync"

	log "github.com/Sirupsen/logrus"
	MQTT "github.com/eclipse/paho.mqtt.golang"
)

const (
	MaxClientIdLen = 10
)

type MqttConf struct {
	Hostname   string
	Port       int
	Username   string
	Password   string
	Cafilepath string
	Topic      string
	Debug      string
}

type MqttClient struct {
	Client     MQTT.Client
	Opts       *MQTT.ClientOptions
	Config     MqttConf
	Subscribed map[string]byte

	mqttChan    chan Message // chan to forwarder
	commandChan chan string  // chan from forwarder
	lock        *sync.Mutex  // use for reconnect
}

//
// with Connects connect to the MQTT broker with Options.
func NewMqttClient(conf MqttConf, mqttChan chan Message, commandChan chan string) (*MqttClient, error) {
	opts := MQTT.NewClientOptions()

	port := conf.Port
	if port == 0 {
		port = 1883
	}
	scheme := "tcp"
	if port == 8883 {
		scheme = "ssl"
	}
	brokerUri := fmt.Sprintf("%s://%s:%d", scheme, conf.Hostname, port)
	log.Infof("Broker URI: %s", brokerUri)
	opts.AddBroker(brokerUri)

	if conf.Username != "" {
		opts.SetUsername(conf.Username)
	}
	if conf.Password != "" {
		opts.SetPassword(conf.Password)
	}

	clientId := getRandomClientId()
	opts.SetClientID(clientId)
	opts.SetAutoReconnect(true)

	topic := conf.Topic
	if strings.HasSuffix(topic, "#") == false {
		topic = topic + "#"
	}
	subscribed := map[string]byte{
		topic: byte(0),
	}

	ret := &MqttClient{
		Config:      conf,
		Subscribed:  subscribed,
		lock:        new(sync.Mutex),
		mqttChan:    mqttChan,
		commandChan: commandChan,
	}
	opts.SetOnConnectHandler(ret.SubscribeOnConnect)
	opts.SetConnectionLostHandler(ret.ConnectionLost)

	ret.Opts = opts

	client, err := ret.Connect(conf, opts, subscribed)
	if err != nil {
		return nil, err
	}
	ret.Client = client

	return ret, nil
}

// connects MQTT broker
func (m MqttClient) Connect(conf MqttConf, opts *MQTT.ClientOptions, subscribed map[string]byte) (MQTT.Client, error) {
	m.Client = MQTT.NewClient(m.Opts)

	log.Info("connecting...")

	if token := m.Client.Connect(); token.Wait() && token.Error() != nil {
		return nil, token.Error()
	}
	return m.Client, nil
}

// getRandomClientId returns randomized ClientId.
func getRandomClientId() string {
	const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
	var bytes = make([]byte, MaxClientIdLen)
	rand.Read(bytes)
	for i, b := range bytes {
		bytes[i] = alphanum[b%byte(len(alphanum))]
	}
	return "mqttforward-" + string(bytes)
}

func (m *MqttClient) SubscribeOnConnect(client MQTT.Client) {
	log.Infof("mqtt connected")
	log.Infof("subscribed: %v", m.Subscribed)

	if len(m.Subscribed) > 0 {
		token := client.SubscribeMultiple(m.Subscribed, m.onMessageReceived)
		token.Wait()
		if token.Error() != nil {
			log.Error(token.Error())
		}
	}
}
func (m *MqttClient) ConnectionLost(client MQTT.Client, reason error) {
	log.Errorf("client disconnected: %s", reason)
}

func (m *MqttClient) Disconnect() error {
	if m.Client.IsConnected() {
		m.Client.Disconnect(20)
		log.Info("client disconnected")
	}
	return nil
}

func (m *MqttClient) onMessageReceived(client MQTT.Client, message MQTT.Message) {
	log.Infof("topic:%s", message.Topic())

	// Remove topic root
	ct := strings.TrimRight(m.Config.Topic, "#")
	topic := strings.Replace(message.Topic(), ct, "", 1)

	chun := Message{
		Topic:   topic,
		Payload: message.Payload(),
	}

	m.mqttChan <- chun
}
<commit_msg>add cert support.<commit_after>package main

import (
	"crypto/rand"
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"io/ioutil"
	"strings"
	"sync"

	log "github.com/Sirupsen/logrus"
	MQTT "github.com/eclipse/paho.mqtt.golang"
)

const (
	MaxClientIdLen = 10
)

type MqttConf struct {
	Hostname   string
	Port       int
	Username   string
	Password   string
	Cafilepath string
	ClientCert string
	PrivateKey string
	Topic      string
	Debug      string
}

type MqttClient struct {
	Client     MQTT.Client
	Opts       *MQTT.ClientOptions
	Config     MqttConf
	Subscribed map[string]byte

	mqttChan    chan Message // chan to forwarder
	commandChan chan string  // chan from forwarder
	lock        *sync.Mutex  // use for reconnect
}

//
// with Connects connect to the MQTT broker with Options.
func NewMqttClient(conf MqttConf, mqttChan chan Message, commandChan chan string) (*MqttClient, error) {
	opts := MQTT.NewClientOptions()

	port := conf.Port
	if port == 0 {
		port = 1883
	}
	scheme := "tcp"
	if port == 8883 {
		scheme = "ssl"
	}
	brokerUri := fmt.Sprintf("%s://%s:%d", scheme, conf.Hostname, port)
	log.Infof("Broker URI: %s", brokerUri)
	opts.AddBroker(brokerUri)

	if conf.Username != "" {
		opts.SetUsername(conf.Username)
	}
	if conf.Password != "" {
		opts.SetPassword(conf.Password)
	}

	clientId := getRandomClientId()
	opts.SetClientID(clientId)
	opts.SetAutoReconnect(true)

	topic := conf.Topic
	if strings.HasSuffix(topic, "#") == false {
		topic = topic + "#"
	}
	subscribed := map[string]byte{
		topic: byte(0),
	}

	tlsConfig, ok, err := makeTlsConfig(conf.Cafilepath, conf.ClientCert, conf.PrivateKey, false)
	if err != nil {
		return nil, err
	}
	if ok {
		opts.SetTLSConfig(tlsConfig)
	}

	ret := &MqttClient{
		Config:      conf,
		Subscribed:  subscribed,
		lock:        new(sync.Mutex),
		mqttChan:    mqttChan,
		commandChan: commandChan,
	}
	opts.SetOnConnectHandler(ret.SubscribeOnConnect)
	opts.SetConnectionLostHandler(ret.ConnectionLost)

	ret.Opts = opts

	client, err := ret.Connect(conf, opts, subscribed)
	if err != nil {
		return nil, err
	}
	ret.Client = client

	return ret, nil
}

// connects MQTT broker
func (m MqttClient) Connect(conf MqttConf, opts *MQTT.ClientOptions, subscribed map[string]byte) (MQTT.Client, error) {
	m.Client = MQTT.NewClient(m.Opts)

	log.Info("connecting...")

	if token := m.Client.Connect(); token.Wait() && token.Error() != nil {
		return nil, token.Error()
	}
	return m.Client, nil
}

// getRandomClientId returns randomized ClientId.
func getRandomClientId() string {
	const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
	var bytes = make([]byte, MaxClientIdLen)
	rand.Read(bytes)
	for i, b := range bytes {
		bytes[i] = alphanum[b%byte(len(alphanum))]
	}
	return "mqttforward-" + string(bytes)
}

func (m *MqttClient) SubscribeOnConnect(client MQTT.Client) {
	log.Infof("mqtt connected")
	log.Infof("subscribed: %v", m.Subscribed)

	if len(m.Subscribed) > 0 {
		token := client.SubscribeMultiple(m.Subscribed, m.onMessageReceived)
		token.Wait()
		if token.Error() != nil {
			log.Error(token.Error())
		}
	}
}
func (m *MqttClient) ConnectionLost(client MQTT.Client, reason error) {
	log.Errorf("client disconnected: %s", reason)
}

func (m *MqttClient) Disconnect() error {
	if m.Client.IsConnected() {
		m.Client.Disconnect(20)
		log.Info("client disconnected")
	}
	return nil
}

func (m *MqttClient) onMessageReceived(client MQTT.Client, message MQTT.Message) {
	log.Infof("topic:%s", message.Topic())

	// Remove topic root
	ct := strings.TrimRight(m.Config.Topic, "#")
	topic := strings.Replace(message.Topic(), ct, "", 1)

	chun := Message{
		Topic:   topic,
		Payload: message.Payload(),
	}

	m.mqttChan <- chun
}

func getCertPool(pemPath string) (*x509.CertPool, error) {
	certs := x509.NewCertPool()

	pemData, err := ioutil.ReadFile(pemPath)
	if err != nil {
		return nil, err
	}
	certs.AppendCertsFromPEM(pemData)
	return certs, nil
}

// makeTlsConfig creats new tls.Config. If returned ok is false, does not need set to MQTToption.
func makeTlsConfig(cafile, cert, key string, insecure bool) (*tls.Config, bool, error) {
	TLSConfig := &tls.Config{InsecureSkipVerify: false}
	var ok bool
	if insecure {
		TLSConfig.InsecureSkipVerify = true
		ok = true
	}
	if cafile != "" {
		certPool, err := getCertPool(cafile)
		if err != nil {
			return nil, false, err
		}
		TLSConfig.RootCAs = certPool
		ok = true
	}
	if cert != "" {
		certPool, err := getCertPool(cert)
		if err != nil {
			return nil, false, err
		}
		TLSConfig.ClientAuth = tls.RequireAndVerifyClientCert
		TLSConfig.ClientCAs = certPool
		ok = true
	}
	if key != "" {
		if cert == "" {
			return nil, false, fmt.Errorf("key specified but cert is not specified")
		}
		cert, err := tls.LoadX509KeyPair(cert, key)
		if err != nil {
			return nil, false, err
		}
		TLSConfig.Certificates = []tls.Certificate{cert}
		ok = true
	}
	return TLSConfig, ok, nil
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"gopkg.in/yaml.v2"
	"io/ioutil"
	"os"
	"path/filepath"
)

const (
	DEFAULT_BUILD_FILE = "build.yml"
)

func ParseCommandLine() (*string, *bool) {
	buildFile := flag.String("file", DEFAULT_BUILD_FILE, "Build file to run")
	buildHelp := flag.Bool("build", false, "Print build help")
	flag.Parse()
	return buildFile, buildHelp
}

func LoadBuildFile(file string) (*Build, error) {
	var build *Build
	source, err := ioutil.ReadFile(file)
	if err != nil {
		return nil, fmt.Errorf("loading build file '%s': %v", file, err)
	}
	err = yaml.Unmarshal(source, &build)
	if err != nil {
		return nil, fmt.Errorf("parsing build file '%s': %v", file, err)
	}
	absBuildFile, err := filepath.Abs(file)
	if err != nil {
		return nil, fmt.Errorf("getting build file path: %v", err)
	}
	build.Init(absBuildFile)
	return build, nil
}

func main() {
	file, help := ParseCommandLine()
	build, err := LoadBuildFile(*file)
	if err != nil {
		PrintError(err.Error())
		os.Exit(1)
	}
	if *help {
		err = build.Help()
		if err != nil {
			PrintError(err.Error())
			os.Exit(2)
		}
	} else {
		err = build.Run()
		if err != nil {
			PrintError(err.Error())
			os.Exit(2)
		}
	}
}
<commit_msg>Misc refactoring<commit_after>package main

import (
	"flag"
	"fmt"
	"gopkg.in/yaml.v2"
	"io/ioutil"
	"os"
	"path/filepath"
)

const (
	DEFAULT_BUILD_FILE = "build.yml"
)

func ParseCommandLine() (*string, *bool) {
	buildFile := flag.String("file", DEFAULT_BUILD_FILE, "Build file to run")
	buildHelp := flag.Bool("build", false, "Print build help")
	flag.Parse()
	return buildFile, buildHelp
}

func LoadBuild(file string) (*Build, error) {
	var build *Build
	source, err := ioutil.ReadFile(file)
	if err != nil {
		return nil, fmt.Errorf("loading build file '%s': %v", file, err)
	}
	err = yaml.Unmarshal(source, &build)
	if err != nil {
		return nil, fmt.Errorf("parsing build file '%s': %v", file, err)
	}
	absBuildFile, err := filepath.Abs(file)
	if err != nil {
		return nil, fmt.Errorf("getting build file path: %v", err)
	}
	build.Init(absBuildFile)
	return build, nil
}

func main() {
	file, help := ParseCommandLine()
	build, err := LoadBuild(*file)
	if err != nil {
		PrintError(err.Error())
		os.Exit(1)
	}
	if *help {
		err = build.Help()
	} else {
		err = build.Run()
	}
	if err != nil {
		PrintError(err.Error())
		os.Exit(2)
	}
}
<|endoftext|>


<commit_before>package yamlpatch

import (
	"fmt"
	"strconv"
)

// NodeType is a type alias
type NodeType int

// NodeTypes
const (
	NodeTypeRaw NodeType = iota
	NodeTypeMap
	NodeTypeSlice
)

// Node holds a YAML document that has not yet been processed into a NodeMap or
// NodeSlice
type Node struct {
	raw       *interface{}
	nodeMap   nodeMap
	nodeSlice nodeSlice
	nodeType  NodeType
}

// NewNode returns a new Node. It expects a pointer to an interface{}
func NewNode(raw *interface{}) *Node {
	return &Node{
		raw: raw,
	}
}

// MarshalYAML implements yaml.Marshaler, and returns the correct interface{}
// to be marshaled
func (n *Node) MarshalYAML() (interface{}, error) {
	switch n.nodeType {
	case NodeTypeRaw:
		return *n.raw, nil
	case NodeTypeMap:
		return n.nodeMap, nil
	case NodeTypeSlice:
		return n.nodeSlice, nil
	default:
		return nil, fmt.Errorf("Unknown type")
	}
}

// UnmarshalYAML implements yaml.Unmarshaler
func (n *Node) UnmarshalYAML(unmarshal func(interface{}) error) error {
	var data interface{}

	err := unmarshal(&data)
	if err != nil {
		return err
	}

	n.raw = &data
	n.nodeType = NodeTypeRaw
	return nil
}

// RawValue returns the interface that the YAML was unmarshaled into
func (n *Node) RawValue() *interface{} {
	return n.raw
}

// Container returns the node as a Container
func (n *Node) Container() (Container, error) {
	switch rt := (*n.raw).(type) {
	case []interface{}:
		if n.nodeSlice != nil {
			return &n.nodeSlice, nil
		}

		array := make(nodeSlice, len(rt))

		for i := range rt {
			array[i] = NewNode(&rt[i])
		}

		n.nodeSlice = array
		n.nodeType = NodeTypeSlice

		return &n.nodeSlice, nil
	case map[interface{}]interface{}:
		doc := make(nodeMap, len(rt))

		for k := range rt {
			v := rt[k]
			doc[k] = NewNode(&v)
		}

		n.nodeMap = doc
		n.nodeType = NodeTypeMap

		return &n.nodeMap, nil
	}

	return nil, fmt.Errorf("don't know how to make container from: %T", *n.raw)
}

// Equal compares the values of the raw interfaces that the YAML was
// unmarshaled into
func (n *Node) Equal(other *Node) bool {
	return *n.raw == *other.raw
}

type nodeMap map[interface{}]*Node

// Set or replace the Node at key with the provided Node
func (n *nodeMap) Set(key string, val *Node) error {
	(*n)[key] = val
	return nil
}

// Add the provided Node at the given key
func (n *nodeMap) Add(key string, val *Node) error {
	(*n)[key] = val
	return nil
}

// Get the node at the given key
func (n *nodeMap) Get(key string) (*Node, error) {
	return (*n)[key], nil
}

// Remove the node at the given key
func (n *nodeMap) Remove(key string) error {
	_, ok := (*n)[key]
	if !ok {
		return fmt.Errorf("Unable to remove nonexistent key: %s", key)
	}

	delete(*n, key)
	return nil
}

type nodeSlice []*Node

// Set the Node at the given index with the provided Node
func (n *nodeSlice) Set(index string, val *Node) error {
	i, err := strconv.Atoi(index)
	if err != nil {
		return err
	}

	sz := len(*n)
	if i+1 > sz {
		sz = i + 1
	}

	ary := make([]*Node, sz)

	cur := *n

	copy(ary, cur)

	if i >= len(ary) {
		return fmt.Errorf("Unable to access invalid index: %d", i)
	}

	ary[i] = val

	*n = ary
	return nil
}

// Add the provided Node at the given index
func (n *nodeSlice) Add(index string, val *Node) error {
	if index == "-" {
		*n = append(*n, val)
		return nil
	}

	i, err := strconv.Atoi(index)
	if err != nil {
		return err
	}

	ary := make([]*Node, len(*n)+1)

	cur := *n

	copy(ary[0:i], cur[0:i])
	ary[i] = val
	copy(ary[i+1:], cur[i:])

	*n = ary
	return nil
}

// Get the node at the given index
func (n *nodeSlice) Get(index string) (*Node, error) {
	i, err := strconv.Atoi(index)
	if err != nil {
		return nil, err
	}

	if i >= len(*n) {
		return nil, fmt.Errorf("Unable to access invalid index: %d", i)
	}

	return (*n)[i], nil
}

// Remove the node at the given index
func (n *nodeSlice) Remove(index string) error {
	i, err := strconv.Atoi(index)
	if err != nil {
		return err
	}

	cur := *n

	if i >= len(cur) {
		return fmt.Errorf("Unable to remove invalid index: %d", i)
	}

	ary := make([]*Node, len(cur)-1)

	copy(ary[0:i], cur[0:i])
	copy(ary[i:], cur[i+1:])

	*n = ary
	return nil

}
<commit_msg>Node holds container, not nodeMap/nodeSlice<commit_after>package yamlpatch

import (
	"fmt"
	"strconv"
)

// NodeType is a type alias
type NodeType int

// NodeTypes
const (
	NodeTypeRaw NodeType = iota
	NodeTypeMap
	NodeTypeSlice
)

// Node holds a YAML document that has not yet been processed into a NodeMap or
// NodeSlice
type Node struct {
	raw       *interface{}
	container Container
	nodeType  NodeType
}

// NewNode returns a new Node. It expects a pointer to an interface{}
func NewNode(raw *interface{}) *Node {
	return &Node{
		raw: raw,
	}
}

// MarshalYAML implements yaml.Marshaler, and returns the correct interface{}
// to be marshaled
func (n *Node) MarshalYAML() (interface{}, error) {
	switch n.nodeType {
	case NodeTypeRaw:
		return *n.raw, nil
	default:
		return n.container, nil
	}
}

// UnmarshalYAML implements yaml.Unmarshaler
func (n *Node) UnmarshalYAML(unmarshal func(interface{}) error) error {
	var data interface{}

	err := unmarshal(&data)
	if err != nil {
		return err
	}

	n.raw = &data
	n.nodeType = NodeTypeRaw
	return nil
}

// RawValue returns the interface that the YAML was unmarshaled into
func (n *Node) RawValue() *interface{} {
	return n.raw
}

// Container returns the node as a Container
func (n *Node) Container() (Container, error) {
	switch rt := (*n.raw).(type) {
	case []interface{}:
		if n.container != nil {
			return n.container, nil
		}

		array := make(nodeSlice, len(rt))

		for i := range rt {
			array[i] = NewNode(&rt[i])
		}

		n.container = &array
		n.nodeType = NodeTypeSlice

		return n.container, nil
	case map[interface{}]interface{}:
		doc := make(nodeMap, len(rt))

		for k := range rt {
			v := rt[k]
			doc[k] = NewNode(&v)
		}

		n.container = &doc
		n.nodeType = NodeTypeMap

		return n.container, nil
	}

	return nil, fmt.Errorf("don't know how to make container from: %T", *n.raw)
}

// Equal compares the values of the raw interfaces that the YAML was
// unmarshaled into
func (n *Node) Equal(other *Node) bool {
	return *n.raw == *other.raw
}

type nodeMap map[interface{}]*Node

// Set or replace the Node at key with the provided Node
func (n *nodeMap) Set(key string, val *Node) error {
	(*n)[key] = val
	return nil
}

// Add the provided Node at the given key
func (n *nodeMap) Add(key string, val *Node) error {
	(*n)[key] = val
	return nil
}

// Get the node at the given key
func (n *nodeMap) Get(key string) (*Node, error) {
	return (*n)[key], nil
}

// Remove the node at the given key
func (n *nodeMap) Remove(key string) error {
	_, ok := (*n)[key]
	if !ok {
		return fmt.Errorf("Unable to remove nonexistent key: %s", key)
	}

	delete(*n, key)
	return nil
}

type nodeSlice []*Node

// Set the Node at the given index with the provided Node
func (n *nodeSlice) Set(index string, val *Node) error {
	i, err := strconv.Atoi(index)
	if err != nil {
		return err
	}

	sz := len(*n)
	if i+1 > sz {
		sz = i + 1
	}

	ary := make([]*Node, sz)

	cur := *n

	copy(ary, cur)

	if i >= len(ary) {
		return fmt.Errorf("Unable to access invalid index: %d", i)
	}

	ary[i] = val

	*n = ary
	return nil
}

// Add the provided Node at the given index
func (n *nodeSlice) Add(index string, val *Node) error {
	if index == "-" {
		*n = append(*n, val)
		return nil
	}

	i, err := strconv.Atoi(index)
	if err != nil {
		return err
	}

	ary := make([]*Node, len(*n)+1)

	cur := *n

	copy(ary[0:i], cur[0:i])
	ary[i] = val
	copy(ary[i+1:], cur[i:])

	*n = ary
	return nil
}

// Get the node at the given index
func (n *nodeSlice) Get(index string) (*Node, error) {
	i, err := strconv.Atoi(index)
	if err != nil {
		return nil, err
	}

	if i >= len(*n) {
		return nil, fmt.Errorf("Unable to access invalid index: %d", i)
	}

	return (*n)[i], nil
}

// Remove the node at the given index
func (n *nodeSlice) Remove(index string) error {
	i, err := strconv.Atoi(index)
	if err != nil {
		return err
	}

	cur := *n

	if i >= len(cur) {
		return fmt.Errorf("Unable to remove invalid index: %d", i)
	}

	ary := make([]*Node, len(cur)-1)

	copy(ary[0:i], cur[0:i])
	copy(ary[i:], cur[i+1:])

	*n = ary
	return nil

}
<|endoftext|>


<commit_before>package graft

import (
	"fmt"
	"math/rand"
	"net"
	"sync"
	"time"

	"golang.org/x/net/context"

	"github.com/lostz/graft/protocol"
	"github.com/uber-go/zap"

	"google.golang.org/grpc"
)

var logger = zap.New(zap.NewTextEncoder())

//Node ...
type Node struct {
	electTimer   *time.Timer
	handler      *ChanHandler
	id           string
	ip           string
	leader       string
	logPath      string
	mu           sync.Mutex
	peers        []string
	quit         chan chan struct{}
	server       *grpc.Server
	state        State
	stateChg     []*StateChange
	term         uint64
	vote         string
	VoteRequests chan *protocol.VoteRequest
	VoteResponse chan *protocol.VoteResponse
	HeartBeats   chan *protocol.HeartbeatRequest
}

func (n *Node) broadcastVote() {
	for _, peer := range n.peers {
		go n.votePeer(peer)
	}
}

func (n *Node) votePeer(peer string) {
	conn, err := grpc.Dial(peer, []grpc.DialOption{grpc.WithTimeout(500 * time.Millisecond), grpc.WithInsecure()}...)
	if err != nil {
		return
	}
	defer conn.Close()
	c := protocol.NewRaftClient(conn)
	_, err = c.ReceiveVoteRequest(context.Background(), &protocol.VoteRequest{Term: n.term, Candidate: n.id})
	if err != nil {
		logger.Error(
			"Vote on",
			zap.String("addr", peer),
			zap.String("err", err.Error()),
		)
		return
	}
	return
}

func (n *Node) broadcastHearbeat() {

	for _, peer := range n.peers {
		go n.heartbeatPeer(peer)

	}
}

func (n *Node) heartbeatPeer(peer string) {
	conn, err := grpc.Dial(peer, []grpc.DialOption{grpc.WithTimeout(1000 * time.Millisecond), grpc.WithInsecure()}...)
	if err != nil {
		return
	}
	defer conn.Close()
	c := protocol.NewRaftClient(conn)
	_, err = c.Heartbeat(context.Background(), &protocol.HeartbeatRequest{Term: n.term, Leader: n.id})
	if err != nil {
		logger.Error(
			"Vote on",
			zap.String("addr", peer),
			zap.String("err", err.Error()),
		)
		return

	}

}

func (n *Node) clearTimers() {
	if n.electTimer != nil {
		n.electTimer.Stop()
		n.electTimer = nil
	}
}

//Close wait until the state is processed
func (n *Node) Close() {
	if n.State() == CLOSED {
		return
	}
	n.server.GracefulStop()
	n.waitOnLoopFinish()
	n.clearTimers()
	n.closeLog()

}

//Heartbeat from leader
func (n *Node) Heartbeat(context context.Context, requset *protocol.HeartbeatRequest) (*protocol.Response, error) {
	if requset.Term < n.term {
		return &protocol.Response{}, nil
	}
	saveState := false
	if n.State() == LEADER {
		n.switchToFollower("")
	}
	n.mu.Lock()
	n.term = requset.Term
	n.vote = ""
	n.mu.Unlock()
	saveState = true
	n.resetElectionTimeout()
	if saveState {
		if err := n.writeState(); err != nil {
			logger.Error(
				"write state",
				zap.Uint64("term", n.term),
				zap.String("error", err.Error()),
			)
		}
	}
	n.switchToFollower(requset.Leader)
	return &protocol.Response{}, nil
}

func (n *Node) handleHeartBeat(hb *protocol.HeartbeatRequest) bool {
	if hb.Term < n.term {
		return false
	}
	saveState := false
	stepDown := false
	if hb.Term > n.term {
		n.term = hb.Term
		n.vote = ""
		stepDown = true
		saveState = true
	}
	if n.State() == CANDIDATE && hb.Term >= n.term {
		n.term = hb.Term
		n.vote = ""
		stepDown = true
		saveState = true
	}
	n.resetElectionTimeout()
	if saveState {
		if err := n.writeState(); err != nil {
			stepDown = true
		}
	}
	return stepDown

}

func (n *Node) handleVoteRequest(vreq *protocol.VoteRequest) bool {
	deny := &protocol.VoteResponse{Term: n.term, Granted: false}
	if vreq.Term < n.term {
		n.sendVoteResponse(vreq.Candidate, deny)
		return false
	}
	saveState := false
	stepDown := false
	if vreq.Term > n.term {
		n.term = vreq.Term
		n.vote = ""
		n.leader = ""
		stepDown = true
		saveState = true
	}
	if n.State() == LEADER && !stepDown {
		n.sendVoteResponse(vreq.Candidate, deny)
		return stepDown
	}
	if n.vote != "" && n.vote != vreq.Candidate {
		n.sendVoteResponse(vreq.Candidate, deny)
		return stepDown
	}
	n.setVote(vreq.Candidate)
	if saveState {
		if err := n.writeState(); err != nil {
			n.setVote("")
			n.sendVoteResponse(vreq.Candidate, deny)
			n.resetElectionTimeout()
			return true
		}

	}
	accept := &protocol.VoteResponse{Term: n.term, Granted: true}
	n.sendVoteResponse(vreq.Candidate, accept)
	n.resetElectionTimeout()
	return stepDown

}

func (n *Node) isRunning() bool {
	n.mu.Lock()
	defer n.mu.Unlock()
	return n.state != CLOSED
}

func (n *Node) loop() {
	for n.isRunning() {
		switch n.State() {
		case FOLLOWER:
			n.runAsFollower()
		case CANDIDATE:
			n.runAsCandidate()
		case LEADER:
			n.runAsLeader()
		}

	}

}

func (n *Node) postStateChange(sc *StateChange) {
	go func() {
		n.handler.StateChange(sc.From, sc.To)
		n.mu.Lock()
		n.stateChg = n.stateChg[1:]
		if len(n.stateChg) > 0 {
			sc := n.stateChg[0]
			n.postStateChange(sc)
		}
		n.mu.Unlock()
	}()
}

func (n *Node) processQuit(q chan struct{}) {
	n.mu.Lock()
	defer n.mu.Unlock()
	n.state = CLOSED
	close(q)
}

func (n *Node) resetElectionTimeout() {
	n.electTimer.Reset(randElectionTimeout())
}

func (n *Node) runAsCandidate() {
	votes := 1
	n.setVote(n.id)
	if err := n.writeState(); err != nil {
		n.switchToFollower("")
		return
	}
	if n.wonElection(votes) {
		n.switchToLeader()
		return
	}
	n.broadcastVote()
	for {
		select {
		case q := <-n.quit:
			n.processQuit(q)
			return
		case <-n.electTimer.C:
			n.switchToCandidate()
			return
		case vresp := <-n.VoteResponse:
			fmt.Println(vresp)
			if vresp.Granted && vresp.Term == n.term {
				votes++
				if n.wonElection(votes) {
					n.switchToLeader()
					return
				}

			}
		case vreq := <-n.VoteRequests:
			fmt.Println(vreq)
			if stepDown := n.handleVoteRequest(vreq); stepDown {
				n.switchToFollower("")
				return
			}
		case hb := <-n.HeartBeats:
			fmt.Println(hb)
			if stepDown := n.handleHeartBeat(hb); stepDown {
				n.switchToFollower(hb.Leader)
				return
			}

		}
	}

}

func (n *Node) runAsFollower() {
	for {
		select {
		case q := <-n.quit:
			n.processQuit(q)
			return
		case <-n.electTimer.C:
			n.switchToCandidate()
			return
		case vreq := <-n.VoteRequests:
			if shouldReturn := n.handleVoteRequest(vreq); shouldReturn {
				return
			}
		case hb := <-n.HeartBeats:
			if n.leader == "" {
				n.setLeader(hb.Leader)
			}
			if stepDown := n.handleHeartBeat(hb); stepDown {
				n.setLeader(hb.Leader)
			}

		}

	}

}

func (n *Node) runAsLeader() {
	hb := time.NewTicker(100 * time.Millisecond)
	defer hb.Stop()
	for {
		select {
		case q := <-n.quit:
			n.processQuit(q)
			return
		case <-hb.C:
			n.broadcastHearbeat()
		case vreq := <-n.VoteRequests:
			if stepDown := n.handleVoteRequest(vreq); stepDown {
				n.switchToFollower("")
				return
			}
		case hb := <-n.HeartBeats:
			if stepDown := n.handleHeartBeat(hb); stepDown {
				n.switchToFollower(hb.Leader)
				return
			}

		}

	}

}

// ReceiveVoteResponse recevie voteresponse
func (n *Node) ReceiveVoteResponse(context context.Context, vresp *protocol.VoteResponse) (*protocol.Response, error) {
	n.VoteResponse <- vresp
	return &protocol.Response{}, nil
}

func (n *Node) sendVoteResponse(peer string, vresp *protocol.VoteResponse) {
	conn, err := grpc.Dial(peer, []grpc.DialOption{grpc.WithTimeout(500 * time.Millisecond), grpc.WithInsecure()}...)
	if err != nil {
		return
	}
	defer conn.Close()
	c := protocol.NewRaftClient(conn)
	_, err = c.ReceiveVoteResponse(context.Background(), vresp)
	if err != nil {
		logger.Error(
			"send vote response",
			zap.String("error", err.Error()),
		)

	}

}

func (n *Node) setTerm(term uint64) {
	n.mu.Lock()
	defer n.mu.Unlock()
	n.term = term
}
func (n *Node) setVote(candidate string) {
	n.mu.Lock()
	defer n.mu.Unlock()
	n.vote = candidate
}

func (n *Node) setLeader(leader string) {
	n.mu.Lock()
	defer n.mu.Unlock()
	n.leader = leader
}

//State node current state
func (n *Node) State() State {
	n.mu.Lock()
	defer n.mu.Unlock()
	return n.state

}

func (n *Node) switchState(state State) {
	if state == n.state {
		return
	}
	old := n.state
	n.state = state
	sc := &StateChange{From: old, To: state}
	n.stateChg = append(n.stateChg, sc)
	if len(n.stateChg) == 1 {
		n.postStateChange(sc)
	}
}

func (n *Node) switchToCandidate() {
	n.mu.Lock()
	defer n.mu.Unlock()
	n.term++
	n.leader = ""
	n.state = CANDIDATE
	n.resetElectionTimeout()
	n.switchState(CANDIDATE)

}
func (n *Node) switchToFollower(leader string) {
	n.mu.Lock()
	defer n.mu.Unlock()
	n.leader = leader
	n.switchState(FOLLOWER)
}

func (n *Node) switchToLeader() {
	n.mu.Lock()
	defer n.mu.Unlock()
	n.leader = n.id
	n.switchState(LEADER)
}

//ReceiveVoteRequest grpc
func (n *Node) ReceiveVoteRequest(context context.Context, vreq *protocol.VoteRequest) (*protocol.Response, error) {
	n.VoteRequests <- vreq
	return &protocol.Response{}, nil
}

func (n *Node) waitOnLoopFinish() {
	q := make(chan struct{})
	n.quit <- q
	<-q
}

func (n *Node) wonElection(votes int) bool {
	return votes >= quorumNeeded(len(n.peers)+1)
}

//NewNode ....
func NewNode(handler *ChanHandler, peers []string, ip, logPath string, port int) (*Node, error) {
	n := &Node{
		ip:      ip,
		state:   FOLLOWER,
		peers:   peers,
		handler: handler,
	}
	n.quit = make(chan chan struct{})
	n.HeartBeats = make(chan *protocol.HeartbeatRequest)
	n.VoteRequests = make(chan *protocol.VoteRequest)
	n.VoteResponse = make(chan *protocol.VoteResponse)
	n.id = fmt.Sprintf("%s:%d", ip, port)
	n.electTimer = time.NewTimer(randElectionTimeout())
	lis, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
	if err != nil {
		return nil, err
	}
	n.logPath = logPath
	g := grpc.NewServer()
	protocol.RegisterRaftServer(g, n)
	n.server = g
	go g.Serve(lis)
	go n.loop()
	return n, nil
}

func quorumNeeded(clusterSize int) int {
	switch clusterSize {
	// Handle 0, but 0 is really an invalid cluster size.
	case 0:
		return 0
	default:
		return clusterSize/2 + 1
	}
}

func randElectionTimeout() time.Duration {
	delta := rand.Int63n(int64(500 * time.Millisecond))
	return (500*time.Millisecond + time.Duration(delta))
}
<commit_msg>remove unsed log<commit_after>package graft

import (
	"fmt"
	"math/rand"
	"net"
	"sync"
	"time"

	"golang.org/x/net/context"

	"github.com/lostz/graft/protocol"
	"github.com/uber-go/zap"

	"google.golang.org/grpc"
)

var logger = zap.New(zap.NewTextEncoder())

//Node ...
type Node struct {
	electTimer   *time.Timer
	handler      *ChanHandler
	id           string
	ip           string
	leader       string
	logPath      string
	mu           sync.Mutex
	peers        []string
	quit         chan chan struct{}
	server       *grpc.Server
	state        State
	stateChg     []*StateChange
	term         uint64
	vote         string
	VoteRequests chan *protocol.VoteRequest
	VoteResponse chan *protocol.VoteResponse
	HeartBeats   chan *protocol.HeartbeatRequest
}

func (n *Node) broadcastVote() {
	for _, peer := range n.peers {
		go n.votePeer(peer)
	}
}

func (n *Node) votePeer(peer string) {
	conn, err := grpc.Dial(peer, []grpc.DialOption{grpc.WithTimeout(500 * time.Millisecond), grpc.WithInsecure()}...)
	if err != nil {
		logger.Error(
			"grpc dial",
			zap.String("addr", peer),
			zap.String("err", err.Error()),
		)
		return
	}
	defer conn.Close()
	c := protocol.NewRaftClient(conn)
	c.ReceiveVoteRequest(context.Background(), &protocol.VoteRequest{Term: n.term, Candidate: n.id})
	return
}

func (n *Node) broadcastHearbeat() {

	for _, peer := range n.peers {
		go n.heartbeatPeer(peer)

	}
}

func (n *Node) heartbeatPeer(peer string) {
	conn, err := grpc.Dial(peer, []grpc.DialOption{grpc.WithTimeout(1000 * time.Millisecond), grpc.WithInsecure()}...)
	if err != nil {
		logger.Error(
			"grpc dial",
			zap.String("addr", peer),
			zap.String("err", err.Error()),
		)
		return
	}
	defer conn.Close()
	c := protocol.NewRaftClient(conn)
	c.Heartbeat(context.Background(), &protocol.HeartbeatRequest{Term: n.term, Leader: n.id})

}

func (n *Node) clearTimers() {
	if n.electTimer != nil {
		n.electTimer.Stop()
		n.electTimer = nil
	}
}

//Close wait until the state is processed
func (n *Node) Close() {
	if n.State() == CLOSED {
		return
	}
	n.server.GracefulStop()
	n.waitOnLoopFinish()
	n.clearTimers()
	n.closeLog()

}

//Heartbeat from leader
func (n *Node) Heartbeat(context context.Context, requset *protocol.HeartbeatRequest) (*protocol.Response, error) {
	if requset.Term < n.term {
		return &protocol.Response{}, nil
	}
	saveState := false
	if n.State() == LEADER {
		n.switchToFollower("")
	}
	n.mu.Lock()
	n.term = requset.Term
	n.vote = ""
	n.mu.Unlock()
	saveState = true
	n.resetElectionTimeout()
	if saveState {
		if err := n.writeState(); err != nil {
			logger.Error(
				"write state",
				zap.Uint64("term", n.term),
				zap.String("error", err.Error()),
			)
		}
	}
	n.switchToFollower(requset.Leader)
	return &protocol.Response{}, nil
}

func (n *Node) handleHeartBeat(hb *protocol.HeartbeatRequest) bool {
	if hb.Term < n.term {
		return false
	}
	saveState := false
	stepDown := false
	if hb.Term > n.term {
		n.term = hb.Term
		n.vote = ""
		stepDown = true
		saveState = true
	}
	if n.State() == CANDIDATE && hb.Term >= n.term {
		n.term = hb.Term
		n.vote = ""
		stepDown = true
		saveState = true
	}
	n.resetElectionTimeout()
	if saveState {
		if err := n.writeState(); err != nil {
			stepDown = true
		}
	}
	return stepDown

}

func (n *Node) handleVoteRequest(vreq *protocol.VoteRequest) bool {
	deny := &protocol.VoteResponse{Term: n.term, Granted: false}
	if vreq.Term < n.term {
		n.sendVoteResponse(vreq.Candidate, deny)
		return false
	}
	saveState := false
	stepDown := false
	if vreq.Term > n.term {
		n.term = vreq.Term
		n.vote = ""
		n.leader = ""
		stepDown = true
		saveState = true
	}
	if n.State() == LEADER && !stepDown {
		n.sendVoteResponse(vreq.Candidate, deny)
		return stepDown
	}
	if n.vote != "" && n.vote != vreq.Candidate {
		n.sendVoteResponse(vreq.Candidate, deny)
		return stepDown
	}
	n.setVote(vreq.Candidate)
	if saveState {
		if err := n.writeState(); err != nil {
			n.setVote("")
			n.sendVoteResponse(vreq.Candidate, deny)
			n.resetElectionTimeout()
			return true
		}

	}
	accept := &protocol.VoteResponse{Term: n.term, Granted: true}
	n.sendVoteResponse(vreq.Candidate, accept)
	n.resetElectionTimeout()
	return stepDown

}

func (n *Node) isRunning() bool {
	n.mu.Lock()
	defer n.mu.Unlock()
	return n.state != CLOSED
}

func (n *Node) loop() {
	for n.isRunning() {
		switch n.State() {
		case FOLLOWER:
			n.runAsFollower()
		case CANDIDATE:
			n.runAsCandidate()
		case LEADER:
			n.runAsLeader()
		}

	}

}

func (n *Node) postStateChange(sc *StateChange) {
	go func() {
		n.handler.StateChange(sc.From, sc.To)
		n.mu.Lock()
		n.stateChg = n.stateChg[1:]
		if len(n.stateChg) > 0 {
			sc := n.stateChg[0]
			n.postStateChange(sc)
		}
		n.mu.Unlock()
	}()
}

func (n *Node) processQuit(q chan struct{}) {
	n.mu.Lock()
	defer n.mu.Unlock()
	n.state = CLOSED
	close(q)
}

func (n *Node) resetElectionTimeout() {
	n.electTimer.Reset(randElectionTimeout())
}

func (n *Node) runAsCandidate() {
	votes := 1
	n.setVote(n.id)
	if err := n.writeState(); err != nil {
		n.switchToFollower("")
		return
	}
	if n.wonElection(votes) {
		n.switchToLeader()
		return
	}
	n.broadcastVote()
	for {
		select {
		case q := <-n.quit:
			n.processQuit(q)
			return
		case <-n.electTimer.C:
			n.switchToCandidate()
			return
		case vresp := <-n.VoteResponse:
			fmt.Println(vresp)
			if vresp.Granted && vresp.Term == n.term {
				votes++
				if n.wonElection(votes) {
					n.switchToLeader()
					return
				}

			}
		case vreq := <-n.VoteRequests:
			fmt.Println(vreq)
			if stepDown := n.handleVoteRequest(vreq); stepDown {
				n.switchToFollower("")
				return
			}
		case hb := <-n.HeartBeats:
			fmt.Println(hb)
			if stepDown := n.handleHeartBeat(hb); stepDown {
				n.switchToFollower(hb.Leader)
				return
			}

		}
	}

}

func (n *Node) runAsFollower() {
	for {
		select {
		case q := <-n.quit:
			n.processQuit(q)
			return
		case <-n.electTimer.C:
			n.switchToCandidate()
			return
		case vreq := <-n.VoteRequests:
			if shouldReturn := n.handleVoteRequest(vreq); shouldReturn {
				return
			}
		case hb := <-n.HeartBeats:
			if n.leader == "" {
				n.setLeader(hb.Leader)
			}
			if stepDown := n.handleHeartBeat(hb); stepDown {
				n.setLeader(hb.Leader)
			}

		}

	}

}

func (n *Node) runAsLeader() {
	hb := time.NewTicker(100 * time.Millisecond)
	defer hb.Stop()
	for {
		select {
		case q := <-n.quit:
			n.processQuit(q)
			return
		case <-hb.C:
			n.broadcastHearbeat()
		case vreq := <-n.VoteRequests:
			if stepDown := n.handleVoteRequest(vreq); stepDown {
				n.switchToFollower("")
				return
			}
		case hb := <-n.HeartBeats:
			if stepDown := n.handleHeartBeat(hb); stepDown {
				n.switchToFollower(hb.Leader)
				return
			}

		}

	}

}

// ReceiveVoteResponse recevie voteresponse
func (n *Node) ReceiveVoteResponse(context context.Context, vresp *protocol.VoteResponse) (*protocol.Response, error) {
	n.VoteResponse <- vresp
	return &protocol.Response{}, nil
}

func (n *Node) sendVoteResponse(peer string, vresp *protocol.VoteResponse) {
	conn, err := grpc.Dial(peer, []grpc.DialOption{grpc.WithTimeout(500 * time.Millisecond), grpc.WithInsecure()}...)
	if err != nil {
		logger.Error(
			"grpc dial",
			zap.String("addr", peer),
			zap.String("err", err.Error()),
		)
		return
	}
	defer conn.Close()
	c := protocol.NewRaftClient(conn)
	c.ReceiveVoteResponse(context.Background(), vresp)
	return

}

func (n *Node) setTerm(term uint64) {
	n.mu.Lock()
	defer n.mu.Unlock()
	n.term = term
}
func (n *Node) setVote(candidate string) {
	n.mu.Lock()
	defer n.mu.Unlock()
	n.vote = candidate
}

func (n *Node) setLeader(leader string) {
	n.mu.Lock()
	defer n.mu.Unlock()
	n.leader = leader
}

//State node current state
func (n *Node) State() State {
	n.mu.Lock()
	defer n.mu.Unlock()
	return n.state

}

func (n *Node) switchState(state State) {
	if state == n.state {
		return
	}
	old := n.state
	n.state = state
	sc := &StateChange{From: old, To: state}
	n.stateChg = append(n.stateChg, sc)
	if len(n.stateChg) == 1 {
		n.postStateChange(sc)
	}
}

func (n *Node) switchToCandidate() {
	n.mu.Lock()
	defer n.mu.Unlock()
	n.term++
	n.leader = ""
	n.state = CANDIDATE
	n.resetElectionTimeout()
	n.switchState(CANDIDATE)

}
func (n *Node) switchToFollower(leader string) {
	n.mu.Lock()
	defer n.mu.Unlock()
	n.leader = leader
	n.switchState(FOLLOWER)
}

func (n *Node) switchToLeader() {
	n.mu.Lock()
	defer n.mu.Unlock()
	n.leader = n.id
	n.switchState(LEADER)
}

//ReceiveVoteRequest grpc
func (n *Node) ReceiveVoteRequest(context context.Context, vreq *protocol.VoteRequest) (*protocol.Response, error) {
	n.VoteRequests <- vreq
	return &protocol.Response{}, nil
}

func (n *Node) waitOnLoopFinish() {
	q := make(chan struct{})
	n.quit <- q
	<-q
}

func (n *Node) wonElection(votes int) bool {
	return votes >= quorumNeeded(len(n.peers)+1)
}

//NewNode ....
func NewNode(handler *ChanHandler, peers []string, ip, logPath string, port int) (*Node, error) {
	n := &Node{
		ip:      ip,
		state:   FOLLOWER,
		peers:   peers,
		handler: handler,
	}
	n.quit = make(chan chan struct{})
	n.HeartBeats = make(chan *protocol.HeartbeatRequest)
	n.VoteRequests = make(chan *protocol.VoteRequest)
	n.VoteResponse = make(chan *protocol.VoteResponse)
	n.id = fmt.Sprintf("%s:%d", ip, port)
	n.electTimer = time.NewTimer(randElectionTimeout())
	lis, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
	if err != nil {
		return nil, err
	}
	n.logPath = logPath
	g := grpc.NewServer()
	protocol.RegisterRaftServer(g, n)
	n.server = g
	go g.Serve(lis)
	go n.loop()
	return n, nil
}

func quorumNeeded(clusterSize int) int {
	switch clusterSize {
	// Handle 0, but 0 is really an invalid cluster size.
	case 0:
		return 0
	default:
		return clusterSize/2 + 1
	}
}

func randElectionTimeout() time.Duration {
	delta := rand.Int63n(int64(500 * time.Millisecond))
	return (500*time.Millisecond + time.Duration(delta))
}
<|endoftext|>