<commit_before>package bitfinex

import (
	"bytes"
	"crypto/tls"
	"encoding/json"
	"log"
	"net/http"
	"reflect"

	"github.com/bitfinexcom/bitfinex-api-go/utils"

	"github.com/gorilla/websocket"
)

// Pairs available
const (
	// Pairs
	BTCUSD = "BTCUSD"
	LTCUSD = "LTCUSD"
	LTCBTC = "LTCBTC"
	ETHUSD = "ETHUSD"
	ETHBTC = "ETHBTC"
	ETCUSD = "ETCUSD"
	ETCBTC = "ETCBTC"
	BFXUSD = "BFXUSD"
	BFXBTC = "BFXBTC"
	ZECUSD = "ZECUSD"
	ZECBTC = "ZECBTC"
	XMRUSD = "XMRUSD"
	XMRBTC = "XMRBTC"
	RRTUSD = "RRTUSD"
	RRTBTC = "RRTBTC"

	// Channels
	ChanBook   = "book"
	ChanTrade  = "trades"
	ChanTicker = "ticker"
)

// WebSocketService allow to connect and receive stream data
// from bitfinex.com ws service.
type WebSocketService struct {
	// http client
	client *Client
	// websocket client
	ws *websocket.Conn
	// special web socket for private messages
	privateWs *websocket.Conn
	// map internal channels to websocket's
	chanMap    map[float64]chan []float64
	subscribes []subscribeToChannel
}

type subscribeMsg struct {
	Event   string  `json:"event"`
	Channel string  `json:"channel"`
	Pair    string  `json:"pair"`
	ChanID  float64 `json:"chanId,omitempty"`
}

type subscribeToChannel struct {
	Channel string
	Pair    string
	Chan    chan []float64
}

// NewWebSocketService returns a WebSocketService using the given client.
func NewWebSocketService(c *Client) *WebSocketService {
	return &WebSocketService{
		client:     c,
		chanMap:    make(map[float64]chan []float64),
		subscribes: make([]subscribeToChannel, 0),
	}
}

// Connect create new bitfinex websocket connection
func (w *WebSocketService) Connect() error {
	var d = websocket.Dialer{
		Subprotocols:    []string{"p1", "p2"},
		ReadBufferSize:  1024,
		WriteBufferSize: 1024,
		Proxy:           http.ProxyFromEnvironment,
	}

	if w.client.WebSocketTLSSkipVerify {
		d.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
	}

	ws, _, err := d.Dial(w.client.WebSocketURL, nil)
	if err != nil {
		return err
	}
	w.ws = ws
	return nil
}

// Close web socket connection
func (w *WebSocketService) Close() {
	w.ws.Close()
}

func (w *WebSocketService) AddSubscribe(channel string, pair string, c chan []float64) {
	s := subscribeToChannel{
		Channel: channel,
		Pair:    pair,
		Chan:    c,
	}
	w.subscribes = append(w.subscribes, s)
}

func (w *WebSocketService) ClearSubscriptions() {
	w.subscribes = make([]subscribeToChannel, 0)
}

func (w *WebSocketService) sendSubscribeMessages() error {
	for _, s := range w.subscribes {
		msg, _ := json.Marshal(subscribeMsg{
			Event:   "subscribe",
			Channel: s.Channel,
			Pair:    s.Pair,
		})

		err := w.ws.WriteMessage(websocket.TextMessage, msg)
		if err != nil {
			return err
		}
	}
	return nil
}

// Subscribe allows to subsribe to channels and watch for new updates.
// This method supports next channels: book, trade, ticker.
func (w *WebSocketService) Subscribe() error {
	// Subscribe to each channel
	if err := w.sendSubscribeMessages(); err != nil {
		return err
	}

	for {
		_, p, err := w.ws.ReadMessage()
		if err != nil {
			return err
		}

		if bytes.Contains(p, []byte("event")) {
			w.handleEventMessage(p)
		} else {
			w.handleDataMessage(p)
		}
	}

	return nil
}

func (w *WebSocketService) handleEventMessage(msg []byte) {
	// Check for first message(event:subscribed)
	event := &subscribeMsg{}
	err := json.Unmarshal(msg, event)

	// Received "subscribed" resposne. Link channels.
	if err == nil {
		for _, k := range w.subscribes {
			if event.Event == "subscribed" && event.Pair == k.Pair && event.Channel == k.Channel {
				w.chanMap[event.ChanID] = k.Chan
			}
		}
	}
}

func (w *WebSocketService) handleDataMessage(msg []byte) {
	// Received payload or data update
	var dataUpdate []float64
	err := json.Unmarshal(msg, &dataUpdate)
	if err == nil {
		chanID := dataUpdate[0]
		// Remove chanID from data update
		// and send message to internal chan
		w.chanMap[chanID] <- dataUpdate[1:]
	}

	// Payload received
	var fullPayload []interface{}
	err = json.Unmarshal(msg, &fullPayload)

	if err != nil {
		log.Println("Error decoding fullPayload", err)
	} else {
		if len(fullPayload) > 3 {
			itemsSlice := fullPayload[3:]
			i, _ := json.Marshal(itemsSlice)
			var item []float64
			err = json.Unmarshal(i, &item)
			if err == nil {
				chanID := fullPayload[0].(float64)
				w.chanMap[chanID] <- item
			}
		} else {
			itemsSlice := fullPayload[1]
			i, _ := json.Marshal(itemsSlice)
			var items [][]float64
			err = json.Unmarshal(i, &items)
			if err == nil {
				chanID := fullPayload[0].(float64)
				for _, v := range items {
					w.chanMap[chanID] <- v
				}
			}
		}
	}
}

/////////////////////////////
// Private websocket messages
/////////////////////////////

type privateConnect struct {
	Event       string `json:"event"`
	APIKey      string `json:"apiKey"`
	AuthSig     string `json:"authSig"`
	AuthPayload string `json:"authPayload"`
}

// Private channel auth response
type privateResponse struct {
	Event  string  `json:"event"`
	Status string  `json:"status"`
	ChanID float64 `json:"chanId,omitempty"`
	UserID float64 `json:"userId"`
}

type TermData struct {
	// Data term. E.g: ps, ws, ou, etc... See official documentation for more details.
	Term string
	// Data will contain different number of elements for each term.
	// Examples:
	// Term: ws, Data: ["exchange","BTC",0.01410829,0]
	// Term: oc, Data: [0,"BTCUSD",0,-0.01,"","CANCELED",270,0,"2015-10-15T11:26:13Z",0]
	Data  []interface{}
	Error string
}

func (c *TermData) HasError() bool {
	return len(c.Error) > 0
}

func (w *WebSocketService) ConnectPrivate(ch chan TermData) {

	var d = websocket.Dialer{
		Subprotocols:    []string{"p1", "p2"},
		ReadBufferSize:  1024,
		WriteBufferSize: 1024,
		Proxy:           http.ProxyFromEnvironment,
	}

	if w.client.WebSocketTLSSkipVerify {
		d.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
	}

	ws, _, err := d.Dial(w.client.WebSocketURL, nil)
	if err != nil {
		ch <- TermData{
			Error: err.Error(),
		}
		return
	}

	nonce, err := utils.GetNonce()
	if err != nil {
		ch <- TermData{Error: err.Error()}
		return
	}

	payload := "AUTH" + nonce
	connectMsg, _ := json.Marshal(&privateConnect{
		Event:       "auth",
		APIKey:      w.client.APIKey,
		AuthSig:     w.client.signPayload(payload),
		AuthPayload: payload,
	})

	// Send auth message
	err = ws.WriteMessage(websocket.TextMessage, connectMsg)
	if err != nil {
		ch <- TermData{
			Error: err.Error(),
		}
		ws.Close()
		return
	}

	var msg string
	for {
		_, p, err := ws.ReadMessage()
		if err != nil {
			ch <- TermData{
				Error: err.Error(),
			}
			ws.Close()
			return
		}

		msg = string(p)
		event := &privateResponse{}
		err = json.Unmarshal([]byte(msg), &event)
		if err != nil {
			// received data update
			var data []interface{}
			err = json.Unmarshal([]byte(msg), &data)
			if err == nil {
				if len(data) == 2 { // Heartbeat
					// XXX: Consider adding a switch to enable/disable passing these along.
					ch <- TermData{Term: data[1].(string)}
					return
				}

				dataTerm := data[1].(string)
				dataList := data[2].([]interface{})

				// check for empty data
				if len(dataList) > 0 {
					if reflect.TypeOf(dataList[0]) == reflect.TypeOf([]interface{}{}) {
						// received list of lists
						for _, v := range dataList {
							ch <- TermData{
								Term: dataTerm,
								Data: v.([]interface{}),
							}
						}
					} else {
						// received flat list
						ch <- TermData{
							Term: dataTerm,
							Data: dataList,
						}
					}
				}
			}
		} else {
			// received auth response
			if event.Event == "auth" && event.Status != "OK" {
				ch <- TermData{
					Error: "Error connecting to private web socket channel.",
				}
				ws.Close()
			}
		}
	}
}
<commit_msg>remove unnecessary string conversions<commit_after>package bitfinex

import (
	"bytes"
	"crypto/tls"
	"encoding/json"
	"log"
	"net/http"
	"reflect"

	"github.com/bitfinexcom/bitfinex-api-go/utils"

	"github.com/gorilla/websocket"
)

// Pairs available
const (
	// Pairs
	BTCUSD = "BTCUSD"
	LTCUSD = "LTCUSD"
	LTCBTC = "LTCBTC"
	ETHUSD = "ETHUSD"
	ETHBTC = "ETHBTC"
	ETCUSD = "ETCUSD"
	ETCBTC = "ETCBTC"
	BFXUSD = "BFXUSD"
	BFXBTC = "BFXBTC"
	ZECUSD = "ZECUSD"
	ZECBTC = "ZECBTC"
	XMRUSD = "XMRUSD"
	XMRBTC = "XMRBTC"
	RRTUSD = "RRTUSD"
	RRTBTC = "RRTBTC"

	// Channels
	ChanBook   = "book"
	ChanTrade  = "trades"
	ChanTicker = "ticker"
)

// WebSocketService allow to connect and receive stream data
// from bitfinex.com ws service.
type WebSocketService struct {
	// http client
	client *Client
	// websocket client
	ws *websocket.Conn
	// special web socket for private messages
	privateWs *websocket.Conn
	// map internal channels to websocket's
	chanMap    map[float64]chan []float64
	subscribes []subscribeToChannel
}

type subscribeMsg struct {
	Event   string  `json:"event"`
	Channel string  `json:"channel"`
	Pair    string  `json:"pair"`
	ChanID  float64 `json:"chanId,omitempty"`
}

type subscribeToChannel struct {
	Channel string
	Pair    string
	Chan    chan []float64
}

// NewWebSocketService returns a WebSocketService using the given client.
func NewWebSocketService(c *Client) *WebSocketService {
	return &WebSocketService{
		client:     c,
		chanMap:    make(map[float64]chan []float64),
		subscribes: make([]subscribeToChannel, 0),
	}
}

// Connect create new bitfinex websocket connection
func (w *WebSocketService) Connect() error {
	var d = websocket.Dialer{
		Subprotocols:    []string{"p1", "p2"},
		ReadBufferSize:  1024,
		WriteBufferSize: 1024,
		Proxy:           http.ProxyFromEnvironment,
	}

	if w.client.WebSocketTLSSkipVerify {
		d.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
	}

	ws, _, err := d.Dial(w.client.WebSocketURL, nil)
	if err != nil {
		return err
	}
	w.ws = ws
	return nil
}

// Close web socket connection
func (w *WebSocketService) Close() {
	w.ws.Close()
}

func (w *WebSocketService) AddSubscribe(channel string, pair string, c chan []float64) {
	s := subscribeToChannel{
		Channel: channel,
		Pair:    pair,
		Chan:    c,
	}
	w.subscribes = append(w.subscribes, s)
}

func (w *WebSocketService) ClearSubscriptions() {
	w.subscribes = make([]subscribeToChannel, 0)
}

func (w *WebSocketService) sendSubscribeMessages() error {
	for _, s := range w.subscribes {
		msg, _ := json.Marshal(subscribeMsg{
			Event:   "subscribe",
			Channel: s.Channel,
			Pair:    s.Pair,
		})

		err := w.ws.WriteMessage(websocket.TextMessage, msg)
		if err != nil {
			return err
		}
	}
	return nil
}

// Subscribe allows to subsribe to channels and watch for new updates.
// This method supports next channels: book, trade, ticker.
func (w *WebSocketService) Subscribe() error {
	// Subscribe to each channel
	if err := w.sendSubscribeMessages(); err != nil {
		return err
	}

	for {
		_, p, err := w.ws.ReadMessage()
		if err != nil {
			return err
		}

		if bytes.Contains(p, []byte("event")) {
			w.handleEventMessage(p)
		} else {
			w.handleDataMessage(p)
		}
	}

	return nil
}

func (w *WebSocketService) handleEventMessage(msg []byte) {
	// Check for first message(event:subscribed)
	event := &subscribeMsg{}
	err := json.Unmarshal(msg, event)

	// Received "subscribed" resposne. Link channels.
	if err == nil {
		for _, k := range w.subscribes {
			if event.Event == "subscribed" && event.Pair == k.Pair && event.Channel == k.Channel {
				w.chanMap[event.ChanID] = k.Chan
			}
		}
	}
}

func (w *WebSocketService) handleDataMessage(msg []byte) {
	// Received payload or data update
	var dataUpdate []float64
	err := json.Unmarshal(msg, &dataUpdate)
	if err == nil {
		chanID := dataUpdate[0]
		// Remove chanID from data update
		// and send message to internal chan
		w.chanMap[chanID] <- dataUpdate[1:]
	}

	// Payload received
	var fullPayload []interface{}
	err = json.Unmarshal(msg, &fullPayload)

	if err != nil {
		log.Println("Error decoding fullPayload", err)
	} else {
		if len(fullPayload) > 3 {
			itemsSlice := fullPayload[3:]
			i, _ := json.Marshal(itemsSlice)
			var item []float64
			err = json.Unmarshal(i, &item)
			if err == nil {
				chanID := fullPayload[0].(float64)
				w.chanMap[chanID] <- item
			}
		} else {
			itemsSlice := fullPayload[1]
			i, _ := json.Marshal(itemsSlice)
			var items [][]float64
			err = json.Unmarshal(i, &items)
			if err == nil {
				chanID := fullPayload[0].(float64)
				for _, v := range items {
					w.chanMap[chanID] <- v
				}
			}
		}
	}
}

/////////////////////////////
// Private websocket messages
/////////////////////////////

type privateConnect struct {
	Event       string `json:"event"`
	APIKey      string `json:"apiKey"`
	AuthSig     string `json:"authSig"`
	AuthPayload string `json:"authPayload"`
}

// Private channel auth response
type privateResponse struct {
	Event  string  `json:"event"`
	Status string  `json:"status"`
	ChanID float64 `json:"chanId,omitempty"`
	UserID float64 `json:"userId"`
}

type TermData struct {
	// Data term. E.g: ps, ws, ou, etc... See official documentation for more details.
	Term string
	// Data will contain different number of elements for each term.
	// Examples:
	// Term: ws, Data: ["exchange","BTC",0.01410829,0]
	// Term: oc, Data: [0,"BTCUSD",0,-0.01,"","CANCELED",270,0,"2015-10-15T11:26:13Z",0]
	Data  []interface{}
	Error string
}

func (c *TermData) HasError() bool {
	return len(c.Error) > 0
}

func (w *WebSocketService) ConnectPrivate(ch chan TermData) {

	var d = websocket.Dialer{
		Subprotocols:    []string{"p1", "p2"},
		ReadBufferSize:  1024,
		WriteBufferSize: 1024,
		Proxy:           http.ProxyFromEnvironment,
	}

	if w.client.WebSocketTLSSkipVerify {
		d.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
	}

	ws, _, err := d.Dial(w.client.WebSocketURL, nil)
	if err != nil {
		ch <- TermData{
			Error: err.Error(),
		}
		return
	}

	nonce, err := utils.GetNonce()
	if err != nil {
		ch <- TermData{Error: err.Error()}
		return
	}

	payload := "AUTH" + nonce
	connectMsg, _ := json.Marshal(&privateConnect{
		Event:       "auth",
		APIKey:      w.client.APIKey,
		AuthSig:     w.client.signPayload(payload),
		AuthPayload: payload,
	})

	// Send auth message
	err = ws.WriteMessage(websocket.TextMessage, connectMsg)
	if err != nil {
		ch <- TermData{
			Error: err.Error(),
		}
		ws.Close()
		return
	}

	for {
		_, p, err := ws.ReadMessage()
		if err != nil {
			ch <- TermData{
				Error: err.Error(),
			}
			ws.Close()
			return
		}

		event := &privateResponse{}
		err = json.Unmarshal(p, &event)
		if err != nil {
			// received data update
			var data []interface{}
			err = json.Unmarshal(p, &data)
			if err == nil {
				if len(data) == 2 { // Heartbeat
					// XXX: Consider adding a switch to enable/disable passing these along.
					ch <- TermData{Term: data[1].(string)}
					return
				}

				dataTerm := data[1].(string)
				dataList := data[2].([]interface{})

				// check for empty data
				if len(dataList) > 0 {
					if reflect.TypeOf(dataList[0]) == reflect.TypeOf([]interface{}{}) {
						// received list of lists
						for _, v := range dataList {
							ch <- TermData{
								Term: dataTerm,
								Data: v.([]interface{}),
							}
						}
					} else {
						// received flat list
						ch <- TermData{
							Term: dataTerm,
							Data: dataList,
						}
					}
				}
			}
		} else {
			// received auth response
			if event.Event == "auth" && event.Status != "OK" {
				ch <- TermData{
					Error: "Error connecting to private web socket channel.",
				}
				ws.Close()
			}
		}
	}
}
<|endoftext|>


<commit_before>package fstools

import (
	"strings"
)

func TrimSuffix(s, suffix string) string {
	if strings.HasSuffix(s, suffix) {
		s = s[:len(s)-len(suffix)]
	}
	return s
}
<commit_msg>added CSVtoArray to stringlib<commit_after>package fstools

import (
	"strings"
)

func TrimSuffix(s, suffix string) string {
	if strings.HasSuffix(s, suffix) {
		s = s[:len(s)-len(suffix)]
	}
	return s
}
func CSVtoArray(tagcsv string) []string {
	var tagcsv_array []string
	if strings.Contains(tagcsv, ",") {
		return strings.Split(tagcsv, ",")
	} else if len(tagcsv) > 0 {
		tagcsv_array = make([]string, 1)
		tagcsv_array[0] = tagcsv
	} else {
		tagcsv_array = make([]string, 0)
	}
	return tagcsv_array
}
<|endoftext|>


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

package rpcpb

import (
	"context"
	"fmt"
	"net/url"
	"time"

	"github.com/coreos/etcd/clientv3"
	pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
	"github.com/coreos/etcd/pkg/transport"

	grpc "google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
)

// DialEtcdGRPCServer creates a raw gRPC connection to an etcd member.
func (m *Member) DialEtcdGRPCServer(opts ...grpc.DialOption) (*grpc.ClientConn, error) {
	dialOpts := []grpc.DialOption{
		grpc.WithTimeout(5 * time.Second),
		grpc.WithBlock(),
	}

	secure := false
	for _, cu := range m.Etcd.AdvertiseClientURLs {
		u, err := url.Parse(cu)
		if err != nil {
			return nil, err
		}
		if u.Scheme == "https" { // TODO: handle unix
			secure = true
		}
	}

	if secure {
		// assume save TLS assets are already stord on disk
		tlsInfo := transport.TLSInfo{
			CertFile:      m.ClientCertPath,
			KeyFile:       m.ClientKeyPath,
			TrustedCAFile: m.ClientTrustedCAPath,

			// TODO: remove this with generated certs
			// only need it for auto TLS
			InsecureSkipVerify: true,
		}
		tlsConfig, err := tlsInfo.ClientConfig()
		if err != nil {
			return nil, err
		}
		creds := credentials.NewTLS(tlsConfig)
		dialOpts = append(dialOpts, grpc.WithTransportCredentials(creds))
	} else {
		dialOpts = append(dialOpts, grpc.WithInsecure())
	}
	dialOpts = append(dialOpts, opts...)
	return grpc.Dial(m.EtcdClientEndpoint, dialOpts...)
}

// CreateEtcdClient creates a client from member.
func (m *Member) CreateEtcdClient(opts ...grpc.DialOption) (*clientv3.Client, error) {
	secure := false
	for _, cu := range m.Etcd.AdvertiseClientURLs {
		u, err := url.Parse(cu)
		if err != nil {
			return nil, err
		}
		if u.Scheme == "https" { // TODO: handle unix
			secure = true
		}
	}

	cfg := clientv3.Config{
		Endpoints:   []string{m.EtcdClientEndpoint},
		DialTimeout: 5 * time.Second,
		DialOptions: opts,
	}
	if secure {
		// assume save TLS assets are already stord on disk
		tlsInfo := transport.TLSInfo{
			CertFile:      m.ClientCertPath,
			KeyFile:       m.ClientKeyPath,
			TrustedCAFile: m.ClientTrustedCAPath,

			// TODO: remove this with generated certs
			// only need it for auto TLS
			InsecureSkipVerify: true,
		}
		tlsConfig, err := tlsInfo.ClientConfig()
		if err != nil {
			return nil, err
		}
		cfg.TLS = tlsConfig
	}
	return clientv3.New(cfg)
}

// CheckCompact ensures that historical data before given revision has been compacted.
func (m *Member) CheckCompact(rev int64) error {
	cli, err := m.CreateEtcdClient()
	if err != nil {
		return fmt.Errorf("%v (%q)", err, m.EtcdClientEndpoint)
	}
	defer cli.Close()

	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	wch := cli.Watch(ctx, "\x00", clientv3.WithFromKey(), clientv3.WithRev(rev-1))
	wr, ok := <-wch
	cancel()

	if !ok {
		return fmt.Errorf("watch channel terminated (endpoint %q)", m.EtcdClientEndpoint)
	}
	if wr.CompactRevision != rev {
		return fmt.Errorf("got compact revision %v, wanted %v (endpoint %q)", wr.CompactRevision, rev, m.EtcdClientEndpoint)
	}

	return nil
}

// Defrag runs defragmentation on this member.
func (m *Member) Defrag() error {
	cli, err := m.CreateEtcdClient()
	if err != nil {
		return fmt.Errorf("%v (%q)", err, m.EtcdClientEndpoint)
	}
	defer cli.Close()

	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
	_, err = cli.Defragment(ctx, m.EtcdClientEndpoint)
	cancel()
	return err
}

// RevHash fetches current revision and hash on this member.
func (m *Member) RevHash() (int64, int64, error) {
	conn, err := m.DialEtcdGRPCServer()
	if err != nil {
		return 0, 0, err
	}
	defer conn.Close()

	mt := pb.NewMaintenanceClient(conn)
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	resp, err := mt.Hash(ctx, &pb.HashRequest{}, grpc.FailFast(false))
	cancel()

	if err != nil {
		return 0, 0, err
	}

	return resp.Header.Revision, int64(resp.Hash), nil
}

// Rev fetches current revision on this member.
func (m *Member) Rev(ctx context.Context) (int64, error) {
	cli, err := m.CreateEtcdClient()
	if err != nil {
		return 0, fmt.Errorf("%v (%q)", err, m.EtcdClientEndpoint)
	}
	defer cli.Close()

	resp, err := cli.Status(ctx, m.EtcdClientEndpoint)
	if err != nil {
		return 0, err
	}
	return resp.Header.Revision, nil
}

// Compact compacts member storage with given revision.
// It blocks until it's physically done.
func (m *Member) Compact(rev int64, timeout time.Duration) error {
	cli, err := m.CreateEtcdClient()
	if err != nil {
		return fmt.Errorf("%v (%q)", err, m.EtcdClientEndpoint)
	}
	defer cli.Close()

	ctx, cancel := context.WithTimeout(context.Background(), timeout)
	_, err = cli.Compact(ctx, rev, clientv3.WithCompactPhysical())
	cancel()
	return err
}

// IsLeader returns true if this member is the current cluster leader.
func (m *Member) IsLeader() (bool, error) {
	cli, err := m.CreateEtcdClient()
	if err != nil {
		return false, fmt.Errorf("%v (%q)", err, m.EtcdClientEndpoint)
	}
	defer cli.Close()

	resp, err := cli.Status(context.Background(), m.EtcdClientEndpoint)
	if err != nil {
		return false, err
	}
	return resp.Header.MemberId == resp.Leader, nil
}

// WriteHealthKey writes a health key to this member.
func (m *Member) WriteHealthKey() error {
	cli, err := m.CreateEtcdClient()
	if err != nil {
		return fmt.Errorf("%v (%q)", err, m.EtcdClientEndpoint)
	}
	defer cli.Close()

	// give enough time-out in case expensive requests (range/delete) are pending
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	_, err = cli.Put(ctx, "health", "good")
	cancel()
	if err != nil {
		return fmt.Errorf("%v (%q)", err, m.EtcdClientEndpoint)
	}
	return nil
}

// FetchSnapshot downloads a snapshot file from this member.
func (m *Member) FetchSnapshot() error {

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

package rpcpb

import (
	"context"
	"fmt"
	"net/url"
	"os"
	"time"

	"github.com/coreos/etcd/clientv3"
	pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
	"github.com/coreos/etcd/pkg/transport"
	"github.com/coreos/etcd/snapshot"

	"github.com/dustin/go-humanize"
	"go.uber.org/zap"
	grpc "google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
)

// DialEtcdGRPCServer creates a raw gRPC connection to an etcd member.
func (m *Member) DialEtcdGRPCServer(opts ...grpc.DialOption) (*grpc.ClientConn, error) {
	dialOpts := []grpc.DialOption{
		grpc.WithTimeout(5 * time.Second),
		grpc.WithBlock(),
	}

	secure := false
	for _, cu := range m.Etcd.AdvertiseClientURLs {
		u, err := url.Parse(cu)
		if err != nil {
			return nil, err
		}
		if u.Scheme == "https" { // TODO: handle unix
			secure = true
		}
	}

	if secure {
		// assume save TLS assets are already stord on disk
		tlsInfo := transport.TLSInfo{
			CertFile:      m.ClientCertPath,
			KeyFile:       m.ClientKeyPath,
			TrustedCAFile: m.ClientTrustedCAPath,

			// TODO: remove this with generated certs
			// only need it for auto TLS
			InsecureSkipVerify: true,
		}
		tlsConfig, err := tlsInfo.ClientConfig()
		if err != nil {
			return nil, err
		}
		creds := credentials.NewTLS(tlsConfig)
		dialOpts = append(dialOpts, grpc.WithTransportCredentials(creds))
	} else {
		dialOpts = append(dialOpts, grpc.WithInsecure())
	}
	dialOpts = append(dialOpts, opts...)
	return grpc.Dial(m.EtcdClientEndpoint, dialOpts...)
}

// CreateEtcdClient creates a client from member.
func (m *Member) CreateEtcdClient(opts ...grpc.DialOption) (*clientv3.Client, error) {
	secure := false
	for _, cu := range m.Etcd.AdvertiseClientURLs {
		u, err := url.Parse(cu)
		if err != nil {
			return nil, err
		}
		if u.Scheme == "https" { // TODO: handle unix
			secure = true
		}
	}

	cfg := clientv3.Config{
		Endpoints:   []string{m.EtcdClientEndpoint},
		DialTimeout: 5 * time.Second,
		DialOptions: opts,
	}
	if secure {
		// assume save TLS assets are already stord on disk
		tlsInfo := transport.TLSInfo{
			CertFile:      m.ClientCertPath,
			KeyFile:       m.ClientKeyPath,
			TrustedCAFile: m.ClientTrustedCAPath,

			// TODO: remove this with generated certs
			// only need it for auto TLS
			InsecureSkipVerify: true,
		}
		tlsConfig, err := tlsInfo.ClientConfig()
		if err != nil {
			return nil, err
		}
		cfg.TLS = tlsConfig
	}
	return clientv3.New(cfg)
}

// CheckCompact ensures that historical data before given revision has been compacted.
func (m *Member) CheckCompact(rev int64) error {
	cli, err := m.CreateEtcdClient()
	if err != nil {
		return fmt.Errorf("%v (%q)", err, m.EtcdClientEndpoint)
	}
	defer cli.Close()

	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	wch := cli.Watch(ctx, "\x00", clientv3.WithFromKey(), clientv3.WithRev(rev-1))
	wr, ok := <-wch
	cancel()

	if !ok {
		return fmt.Errorf("watch channel terminated (endpoint %q)", m.EtcdClientEndpoint)
	}
	if wr.CompactRevision != rev {
		return fmt.Errorf("got compact revision %v, wanted %v (endpoint %q)", wr.CompactRevision, rev, m.EtcdClientEndpoint)
	}

	return nil
}

// Defrag runs defragmentation on this member.
func (m *Member) Defrag() error {
	cli, err := m.CreateEtcdClient()
	if err != nil {
		return fmt.Errorf("%v (%q)", err, m.EtcdClientEndpoint)
	}
	defer cli.Close()

	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
	_, err = cli.Defragment(ctx, m.EtcdClientEndpoint)
	cancel()
	return err
}

// RevHash fetches current revision and hash on this member.
func (m *Member) RevHash() (int64, int64, error) {
	conn, err := m.DialEtcdGRPCServer()
	if err != nil {
		return 0, 0, err
	}
	defer conn.Close()

	mt := pb.NewMaintenanceClient(conn)
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	resp, err := mt.Hash(ctx, &pb.HashRequest{}, grpc.FailFast(false))
	cancel()

	if err != nil {
		return 0, 0, err
	}

	return resp.Header.Revision, int64(resp.Hash), nil
}

// Rev fetches current revision on this member.
func (m *Member) Rev(ctx context.Context) (int64, error) {
	cli, err := m.CreateEtcdClient()
	if err != nil {
		return 0, fmt.Errorf("%v (%q)", err, m.EtcdClientEndpoint)
	}
	defer cli.Close()

	resp, err := cli.Status(ctx, m.EtcdClientEndpoint)
	if err != nil {
		return 0, err
	}
	return resp.Header.Revision, nil
}

// Compact compacts member storage with given revision.
// It blocks until it's physically done.
func (m *Member) Compact(rev int64, timeout time.Duration) error {
	cli, err := m.CreateEtcdClient()
	if err != nil {
		return fmt.Errorf("%v (%q)", err, m.EtcdClientEndpoint)
	}
	defer cli.Close()

	ctx, cancel := context.WithTimeout(context.Background(), timeout)
	_, err = cli.Compact(ctx, rev, clientv3.WithCompactPhysical())
	cancel()
	return err
}

// IsLeader returns true if this member is the current cluster leader.
func (m *Member) IsLeader() (bool, error) {
	cli, err := m.CreateEtcdClient()
	if err != nil {
		return false, fmt.Errorf("%v (%q)", err, m.EtcdClientEndpoint)
	}
	defer cli.Close()

	resp, err := cli.Status(context.Background(), m.EtcdClientEndpoint)
	if err != nil {
		return false, err
	}
	return resp.Header.MemberId == resp.Leader, nil
}

// WriteHealthKey writes a health key to this member.
func (m *Member) WriteHealthKey() error {
	cli, err := m.CreateEtcdClient()
	if err != nil {
		return fmt.Errorf("%v (%q)", err, m.EtcdClientEndpoint)
	}
	defer cli.Close()

	// give enough time-out in case expensive requests (range/delete) are pending
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	_, err = cli.Put(ctx, "health", "good")
	cancel()
	if err != nil {
		return fmt.Errorf("%v (%q)", err, m.EtcdClientEndpoint)
	}
	return nil
}

// FetchSnapshot downloads a snapshot file from this member.
func (m *Member) FetchSnapshot(lg *zap.Logger) (err error) {
	// remove existing snapshot first
	if err = os.Remove(m.SnapshotPath); err != nil {
		return err
	}

	var cli *clientv3.Client
	cli, err = m.CreateEtcdClient()
	if err != nil {
		return fmt.Errorf("%v (%q)", err, m.EtcdClientEndpoint)
	}
	defer cli.Close()

	now := time.Now()
	mgr := snapshot.NewV3(cli, lg)
	if err = mgr.Save(context.Background(), m.SnapshotPath); err != nil {
		return err
	}
	took := time.Since(now)

	var fi os.FileInfo
	fi, err = os.Stat(m.SnapshotPath)
	if err != nil {
		return err
	}

	var st snapshot.Status
	st, err = mgr.Status(m.SnapshotPath)
	if err != nil {
		return err
	}

	lg.Info(
		"snapshot saved",
		zap.String("snapshot-path", m.SnapshotPath),
		zap.String("snapshot-file-size", humanize.Bytes(uint64(fi.Size()))),
		zap.String("snapshot-total-size", humanize.Bytes(uint64(st.TotalSize))),
		zap.Int("snapshot-total-key", st.TotalKey),
		zap.Uint32("snapshot-hash", st.Hash),
		zap.Int64("snapshot-revision", st.Revision),
		zap.Duration("took", took),
	)
	return nil
}
<|endoftext|>


<commit_before>package nodefs

// This file contains the internal logic of the
// FileSystemConnector. The functions for satisfying the raw interface
// are in fsops.go

import (
	"log"
	"path/filepath"
	"strings"
	"time"
	"unsafe"

	"github.com/hanwen/go-fuse/fuse"
)

// Tests should set to true.
var paranoia = false

// FilesystemConnector translates the raw FUSE protocol (serialized
// structs of uint32/uint64) to operations on Go objects representing
// files and directories.
type FileSystemConnector struct {
	debug bool

	// Callbacks for talking back to the kernel.
	server *fuse.Server

	// Translate between uint64 handles and *Inode.
	inodeMap handleMap

	// The root of the FUSE file system.
	rootNode *Inode
}

// NewOptions generates FUSE options that correspond to libfuse's
// defaults.
func NewOptions() *Options {
	return &Options{
		NegativeTimeout: 0,
		AttrTimeout:     time.Second,
		EntryTimeout:    time.Second,
		Owner:           fuse.CurrentOwner(),
	}
}

// NewFileSystemConnector creates a FileSystemConnector with the given
// options.
func NewFileSystemConnector(root Node, opts *Options) (c *FileSystemConnector) {
	c = new(FileSystemConnector)
	if opts == nil {
		opts = NewOptions()
	}
	c.inodeMap = newPortableHandleMap()
	c.rootNode = newInode(true, root)

	c.verify()
	c.mountRoot(opts)

	// FUSE does not issue a LOOKUP for 1 (obviously), but it does
	// issue a forget.  This lookupUpdate is to make the counts match.
	c.lookupUpdate(c.rootNode)

	return c
}

// Server returns the fuse.Server that talking to the kernel.
func (c *FileSystemConnector) Server() *fuse.Server {
	return c.server
}

// SetDebug toggles printing of debug information.
func (c *FileSystemConnector) SetDebug(debug bool) {
	c.debug = debug
}

// This verifies invariants of the data structure.  This routine
// acquires tree locks as it walks the inode tree.
func (c *FileSystemConnector) verify() {
	if !paranoia {
		return
	}
	root := c.rootNode
	root.verify(c.rootNode.mountPoint)
}

// childLookup fills entry information for a newly created child inode
func (c *rawBridge) childLookup(out *fuse.EntryOut, n *Inode, context *fuse.Context) {
	n.Node().GetAttr((*fuse.Attr)(&out.Attr), nil, context)
	n.mount.fillEntry(out)
	out.NodeId, out.Generation = c.fsConn().lookupUpdate(n)
	if out.Ino == 0 {
		out.Ino = out.NodeId
	}
	if out.Nlink == 0 {
		// With Nlink == 0, newer kernels will refuse link
		// operations.
		out.Nlink = 1
	}
}

func (c *rawBridge) toInode(nodeid uint64) *Inode {
	if nodeid == fuse.FUSE_ROOT_ID {
		return c.rootNode
	}
	i := (*Inode)(unsafe.Pointer(c.inodeMap.Decode(nodeid)))
	return i
}

// Must run outside treeLock.  Returns the nodeId and generation.
func (c *FileSystemConnector) lookupUpdate(node *Inode) (id, generation uint64) {
	id, generation = c.inodeMap.Register(&node.handled)
	c.verify()
	return
}

// Must run outside treeLock.
func (c *FileSystemConnector) forgetUpdate(nodeID uint64, forgetCount int) {
	if nodeID == fuse.FUSE_ROOT_ID {
		c.rootNode.Node().OnUnmount()

		// We never got a lookup for root, so don't try to
		// forget root.
		return
	}

	if forgotten, handled := c.inodeMap.Forget(nodeID, forgetCount); forgotten {
		node := (*Inode)(unsafe.Pointer(handled))
		node.mount.treeLock.Lock()
		c.recursiveConsiderDropInode(node)
		node.mount.treeLock.Unlock()
	}
	// TODO - try to drop children even forget was not successful.
	c.verify()
}

// InodeCount returns the number of inodes registered with the kernel.
func (c *FileSystemConnector) InodeHandleCount() int {
	return c.inodeMap.Count()
}

// Must hold treeLock.

func (c *FileSystemConnector) recursiveConsiderDropInode(n *Inode) (drop bool) {
	delChildren := []string{}
	for k, v := range n.children {
		// Only consider children from the same mount, or
		// already unmounted mountpoints.
		if v.mountPoint == nil && c.recursiveConsiderDropInode(v) {
			delChildren = append(delChildren, k)
		}
	}
	for _, k := range delChildren {
		ch := n.rmChild(k)
		if ch == nil {
			log.Panicf("trying to del child %q, but not present", k)
		}
		ch.fsInode.OnForget()
	}

	if len(n.children) > 0 || !n.Node().Deletable() {
		return false
	}
	if n == c.rootNode || n.mountPoint != nil {
		return false
	}

	n.openFilesMutex.Lock()
	ok := len(n.openFiles) == 0
	n.openFilesMutex.Unlock()

	return ok
}

// Finds a node within the currently known inodes, returns the last
// known node and the remaining unknown path components.  If parent is
// nil, start from FUSE mountpoint.
func (c *FileSystemConnector) Node(parent *Inode, fullPath string) (*Inode, []string) {
	if parent == nil {
		parent = c.rootNode
	}
	if fullPath == "" {
		return parent, nil
	}

	sep := string(filepath.Separator)
	fullPath = strings.TrimLeft(filepath.Clean(fullPath), sep)
	comps := strings.Split(fullPath, sep)

	node := parent
	if node.mountPoint == nil {
		node.mount.treeLock.RLock()
		defer node.mount.treeLock.RUnlock()
	}

	for i, component := range comps {
		if len(component) == 0 {
			continue
		}

		if node.mountPoint != nil {
			node.mount.treeLock.RLock()
			defer node.mount.treeLock.RUnlock()
		}

		next := node.children[component]
		if next == nil {
			return node, comps[i:]
		}
		node = next
	}

	return node, nil
}

// Follows the path from the given parent, doing lookups as
// necesary. The path should be '/' separated without leading slash.
func (c *FileSystemConnector) LookupNode(parent *Inode, path string) *Inode {
	if path == "" {
		return parent
	}

	components := strings.Split(path, "/")
	for _, r := range components {
		var a fuse.Attr
		// This will not affect inode ID lookup counts, which
		// are only update in response to kernel requests.
		var dummy fuse.InHeader
		child, _ := c.internalLookup(&a, parent, r, &dummy)
		if child == nil {
			return nil
		}

		parent = child
	}

	return parent
}

func (c *FileSystemConnector) mountRoot(opts *Options) {
	c.rootNode.mountFs(opts)
	c.rootNode.mount.connector = c
	c.rootNode.Node().OnMount(c)
	c.verify()
}

// Mount() generates a synthetic directory node, and mounts the file
// system there.  If opts is nil, the mount options of the root file
// system are inherited.  The encompassing filesystem should pretend
// the mount point does not exist.
//
// It returns ENOENT if the directory containing the mount point does
// not exist, and EBUSY if the intended mount point already exists.
func (c *FileSystemConnector) Mount(parent *Inode, name string, root Node, opts *Options) fuse.Status {
	defer c.verify()
	parent.mount.treeLock.Lock()
	defer parent.mount.treeLock.Unlock()
	node := parent.children[name]
	if node != nil {
		return fuse.EBUSY
	}

	node = newInode(true, root)
	if opts == nil {
		opts = c.rootNode.mountPoint.options
	}

	node.mountFs(opts)
	node.mount.connector = c
	parent.addChild(name, node)

	node.mountPoint.parentInode = parent
	if c.debug {
		log.Printf("Mount %T on subdir %s, parent %d", node,
			name, c.inodeMap.Handle(&parent.handled))
	}
	node.Node().OnMount(c)
	return fuse.OK
}

// Unmount() tries to unmount the given inode.  It returns EINVAL if the
// path does not exist, or is not a mount point, and EBUSY if there
// are open files or submounts below this node.
func (c *FileSystemConnector) Unmount(node *Inode) fuse.Status {
	// TODO - racy.
	if node.mountPoint == nil {
		log.Println("not a mountpoint:", c.inodeMap.Handle(&node.handled))
		return fuse.EINVAL
	}

	nodeID := c.inodeMap.Handle(&node.handled)

	// Must lock parent to update tree structure.
	parentNode := node.mountPoint.parentInode
	parentNode.mount.treeLock.Lock()
	defer parentNode.mount.treeLock.Unlock()

	mount := node.mountPoint
	name := node.mountPoint.mountName()
	if mount.openFiles.Count() > 0 {
		return fuse.EBUSY
	}

	node.mount.treeLock.Lock()
	defer node.mount.treeLock.Unlock()

	if mount.mountInode != node {
		log.Panicf("got two different mount inodes %v vs %v",
			c.inodeMap.Handle(&mount.mountInode.handled),
			c.inodeMap.Handle(&node.handled))
	}

	if !node.canUnmount() {
		return fuse.EBUSY
	}

	delete(parentNode.children, name)
	node.Node().OnUnmount()

	parentId := c.inodeMap.Handle(&parentNode.handled)
	if parentNode == c.rootNode {
		// TODO - test coverage. Currently covered by zipfs/multizip_test.go
		parentId = fuse.FUSE_ROOT_ID
	}

	// We have to wait until the kernel has forgotten the
	// mountpoint, so the write to node.mountPoint is no longer
	// racy.
	mount.treeLock.Unlock()
	parentNode.mount.treeLock.Unlock()
	code := c.server.DeleteNotify(parentId, nodeID, name)

	if code.Ok() {
		delay := 100 * time.Microsecond

		for {
			// This operation is rare, so we kludge it to avoid
			// contention.
			time.Sleep(delay)
			delay = delay * 2
			if !c.inodeMap.Has(nodeID) {
				break
			}

			if delay >= time.Second {
				// We limit the wait at one second. If
				// it takes longer, something else is
				// amiss, and we would be waiting forever.
				log.Println("kernel did not issue FORGET for node on Unmount.")
				break
			}
		}

	}

	parentNode.mount.treeLock.Lock()
	mount.treeLock.Lock()
	mount.mountInode = nil
	node.mountPoint = nil

	return fuse.OK
}

// FileNotify notifies the kernel that data and metadata of this inode
// has changed.  After this call completes, the kernel will issue a
// new GetAttr requests for metadata and new Read calls for content.
// Use negative offset for metadata-only invalidation, and zero-length
// for invalidating all content.
func (c *FileSystemConnector) FileNotify(node *Inode, off int64, length int64) fuse.Status {
	var nId uint64
	if node == c.rootNode {
		nId = fuse.FUSE_ROOT_ID
	} else {
		nId = c.inodeMap.Handle(&node.handled)
	}

	if nId == 0 {
		return fuse.OK
	}
	return c.server.InodeNotify(nId, off, length)
}

// EntryNotify makes the kernel forget the entry data from the given
// name from a directory.  After this call, the kernel will issue a
// new lookup request for the given name when necessary. No filesystem
// related locks should be held when calling this.
func (c *FileSystemConnector) EntryNotify(node *Inode, name string) fuse.Status {
	var nId uint64
	if node == c.rootNode {
		nId = fuse.FUSE_ROOT_ID
	} else {
		nId = c.inodeMap.Handle(&node.handled)
	}

	if nId == 0 {
		return fuse.OK
	}
	return c.server.EntryNotify(nId, name)
}

// DeleteNotify signals to the kernel that the named entry in dir for
// the child disappeared. No filesystem related locks should be held
// when calling this.
func (c *FileSystemConnector) DeleteNotify(dir *Inode, child *Inode, name string) fuse.Status {
	var nId uint64

	if dir == c.rootNode {
		nId = fuse.FUSE_ROOT_ID
	} else {
		nId = c.inodeMap.Handle(&dir.handled)
	}

	if nId == 0 {
		return fuse.OK
	}

	chId := c.inodeMap.Handle(&child.handled)

	return c.server.DeleteNotify(nId, chId, name)
}
<commit_msg>nodefs: run OnMount outside of the parent treelock.<commit_after>package nodefs

// This file contains the internal logic of the
// FileSystemConnector. The functions for satisfying the raw interface
// are in fsops.go

import (
	"log"
	"path/filepath"
	"strings"
	"time"
	"unsafe"

	"github.com/hanwen/go-fuse/fuse"
)

// Tests should set to true.
var paranoia = false

// FilesystemConnector translates the raw FUSE protocol (serialized
// structs of uint32/uint64) to operations on Go objects representing
// files and directories.
type FileSystemConnector struct {
	debug bool

	// Callbacks for talking back to the kernel.
	server *fuse.Server

	// Translate between uint64 handles and *Inode.
	inodeMap handleMap

	// The root of the FUSE file system.
	rootNode *Inode
}

// NewOptions generates FUSE options that correspond to libfuse's
// defaults.
func NewOptions() *Options {
	return &Options{
		NegativeTimeout: 0,
		AttrTimeout:     time.Second,
		EntryTimeout:    time.Second,
		Owner:           fuse.CurrentOwner(),
	}
}

// NewFileSystemConnector creates a FileSystemConnector with the given
// options.
func NewFileSystemConnector(root Node, opts *Options) (c *FileSystemConnector) {
	c = new(FileSystemConnector)
	if opts == nil {
		opts = NewOptions()
	}
	c.inodeMap = newPortableHandleMap()
	c.rootNode = newInode(true, root)

	c.verify()
	c.mountRoot(opts)

	// FUSE does not issue a LOOKUP for 1 (obviously), but it does
	// issue a forget.  This lookupUpdate is to make the counts match.
	c.lookupUpdate(c.rootNode)

	return c
}

// Server returns the fuse.Server that talking to the kernel.
func (c *FileSystemConnector) Server() *fuse.Server {
	return c.server
}

// SetDebug toggles printing of debug information.
func (c *FileSystemConnector) SetDebug(debug bool) {
	c.debug = debug
}

// This verifies invariants of the data structure.  This routine
// acquires tree locks as it walks the inode tree.
func (c *FileSystemConnector) verify() {
	if !paranoia {
		return
	}
	root := c.rootNode
	root.verify(c.rootNode.mountPoint)
}

// childLookup fills entry information for a newly created child inode
func (c *rawBridge) childLookup(out *fuse.EntryOut, n *Inode, context *fuse.Context) {
	n.Node().GetAttr((*fuse.Attr)(&out.Attr), nil, context)
	n.mount.fillEntry(out)
	out.NodeId, out.Generation = c.fsConn().lookupUpdate(n)
	if out.Ino == 0 {
		out.Ino = out.NodeId
	}
	if out.Nlink == 0 {
		// With Nlink == 0, newer kernels will refuse link
		// operations.
		out.Nlink = 1
	}
}

func (c *rawBridge) toInode(nodeid uint64) *Inode {
	if nodeid == fuse.FUSE_ROOT_ID {
		return c.rootNode
	}
	i := (*Inode)(unsafe.Pointer(c.inodeMap.Decode(nodeid)))
	return i
}

// Must run outside treeLock.  Returns the nodeId and generation.
func (c *FileSystemConnector) lookupUpdate(node *Inode) (id, generation uint64) {
	id, generation = c.inodeMap.Register(&node.handled)
	c.verify()
	return
}

// Must run outside treeLock.
func (c *FileSystemConnector) forgetUpdate(nodeID uint64, forgetCount int) {
	if nodeID == fuse.FUSE_ROOT_ID {
		c.rootNode.Node().OnUnmount()

		// We never got a lookup for root, so don't try to
		// forget root.
		return
	}

	if forgotten, handled := c.inodeMap.Forget(nodeID, forgetCount); forgotten {
		node := (*Inode)(unsafe.Pointer(handled))
		node.mount.treeLock.Lock()
		c.recursiveConsiderDropInode(node)
		node.mount.treeLock.Unlock()
	}
	// TODO - try to drop children even forget was not successful.
	c.verify()
}

// InodeCount returns the number of inodes registered with the kernel.
func (c *FileSystemConnector) InodeHandleCount() int {
	return c.inodeMap.Count()
}

// Must hold treeLock.

func (c *FileSystemConnector) recursiveConsiderDropInode(n *Inode) (drop bool) {
	delChildren := []string{}
	for k, v := range n.children {
		// Only consider children from the same mount, or
		// already unmounted mountpoints.
		if v.mountPoint == nil && c.recursiveConsiderDropInode(v) {
			delChildren = append(delChildren, k)
		}
	}
	for _, k := range delChildren {
		ch := n.rmChild(k)
		if ch == nil {
			log.Panicf("trying to del child %q, but not present", k)
		}
		ch.fsInode.OnForget()
	}

	if len(n.children) > 0 || !n.Node().Deletable() {
		return false
	}
	if n == c.rootNode || n.mountPoint != nil {
		return false
	}

	n.openFilesMutex.Lock()
	ok := len(n.openFiles) == 0
	n.openFilesMutex.Unlock()

	return ok
}

// Finds a node within the currently known inodes, returns the last
// known node and the remaining unknown path components.  If parent is
// nil, start from FUSE mountpoint.
func (c *FileSystemConnector) Node(parent *Inode, fullPath string) (*Inode, []string) {
	if parent == nil {
		parent = c.rootNode
	}
	if fullPath == "" {
		return parent, nil
	}

	sep := string(filepath.Separator)
	fullPath = strings.TrimLeft(filepath.Clean(fullPath), sep)
	comps := strings.Split(fullPath, sep)

	node := parent
	if node.mountPoint == nil {
		node.mount.treeLock.RLock()
		defer node.mount.treeLock.RUnlock()
	}

	for i, component := range comps {
		if len(component) == 0 {
			continue
		}

		if node.mountPoint != nil {
			node.mount.treeLock.RLock()
			defer node.mount.treeLock.RUnlock()
		}

		next := node.children[component]
		if next == nil {
			return node, comps[i:]
		}
		node = next
	}

	return node, nil
}

// Follows the path from the given parent, doing lookups as
// necesary. The path should be '/' separated without leading slash.
func (c *FileSystemConnector) LookupNode(parent *Inode, path string) *Inode {
	if path == "" {
		return parent
	}

	components := strings.Split(path, "/")
	for _, r := range components {
		var a fuse.Attr
		// This will not affect inode ID lookup counts, which
		// are only update in response to kernel requests.
		var dummy fuse.InHeader
		child, _ := c.internalLookup(&a, parent, r, &dummy)
		if child == nil {
			return nil
		}

		parent = child
	}

	return parent
}

func (c *FileSystemConnector) mountRoot(opts *Options) {
	c.rootNode.mountFs(opts)
	c.rootNode.mount.connector = c
	c.rootNode.Node().OnMount(c)
	c.verify()
}

// Mount() generates a synthetic directory node, and mounts the file
// system there.  If opts is nil, the mount options of the root file
// system are inherited.  The encompassing filesystem should pretend
// the mount point does not exist.
//
// It returns ENOENT if the directory containing the mount point does
// not exist, and EBUSY if the intended mount point already exists.
func (c *FileSystemConnector) Mount(parent *Inode, name string, root Node, opts *Options) fuse.Status {
	node, code := c.lockMount(parent, name, root, opts)
	if !code.Ok() {
		return code
	}

	node.Node().OnMount(c)
	return code
}

func (c *FileSystemConnector) lockMount(parent *Inode, name string, root Node, opts *Options) (*Inode, fuse.Status) {
	defer c.verify()
	parent.mount.treeLock.Lock()
	defer parent.mount.treeLock.Unlock()
	node := parent.children[name]
	if node != nil {
		return nil, fuse.EBUSY
	}

	node = newInode(true, root)
	if opts == nil {
		opts = c.rootNode.mountPoint.options
	}

	node.mountFs(opts)
	node.mount.connector = c
	parent.addChild(name, node)

	node.mountPoint.parentInode = parent
	if c.debug {
		log.Printf("Mount %T on subdir %s, parent %d", node,
			name, c.inodeMap.Handle(&parent.handled))
	}
	return node, fuse.OK
}

// Unmount() tries to unmount the given inode.  It returns EINVAL if the
// path does not exist, or is not a mount point, and EBUSY if there
// are open files or submounts below this node.
func (c *FileSystemConnector) Unmount(node *Inode) fuse.Status {
	// TODO - racy.
	if node.mountPoint == nil {
		log.Println("not a mountpoint:", c.inodeMap.Handle(&node.handled))
		return fuse.EINVAL
	}

	nodeID := c.inodeMap.Handle(&node.handled)

	// Must lock parent to update tree structure.
	parentNode := node.mountPoint.parentInode
	parentNode.mount.treeLock.Lock()
	defer parentNode.mount.treeLock.Unlock()

	mount := node.mountPoint
	name := node.mountPoint.mountName()
	if mount.openFiles.Count() > 0 {
		return fuse.EBUSY
	}

	node.mount.treeLock.Lock()
	defer node.mount.treeLock.Unlock()

	if mount.mountInode != node {
		log.Panicf("got two different mount inodes %v vs %v",
			c.inodeMap.Handle(&mount.mountInode.handled),
			c.inodeMap.Handle(&node.handled))
	}

	if !node.canUnmount() {
		return fuse.EBUSY
	}

	delete(parentNode.children, name)
	node.Node().OnUnmount()

	parentId := c.inodeMap.Handle(&parentNode.handled)
	if parentNode == c.rootNode {
		// TODO - test coverage. Currently covered by zipfs/multizip_test.go
		parentId = fuse.FUSE_ROOT_ID
	}

	// We have to wait until the kernel has forgotten the
	// mountpoint, so the write to node.mountPoint is no longer
	// racy.
	mount.treeLock.Unlock()
	parentNode.mount.treeLock.Unlock()
	code := c.server.DeleteNotify(parentId, nodeID, name)

	if code.Ok() {
		delay := 100 * time.Microsecond

		for {
			// This operation is rare, so we kludge it to avoid
			// contention.
			time.Sleep(delay)
			delay = delay * 2
			if !c.inodeMap.Has(nodeID) {
				break
			}

			if delay >= time.Second {
				// We limit the wait at one second. If
				// it takes longer, something else is
				// amiss, and we would be waiting forever.
				log.Println("kernel did not issue FORGET for node on Unmount.")
				break
			}
		}

	}

	parentNode.mount.treeLock.Lock()
	mount.treeLock.Lock()
	mount.mountInode = nil
	node.mountPoint = nil

	return fuse.OK
}

// FileNotify notifies the kernel that data and metadata of this inode
// has changed.  After this call completes, the kernel will issue a
// new GetAttr requests for metadata and new Read calls for content.
// Use negative offset for metadata-only invalidation, and zero-length
// for invalidating all content.
func (c *FileSystemConnector) FileNotify(node *Inode, off int64, length int64) fuse.Status {
	var nId uint64
	if node == c.rootNode {
		nId = fuse.FUSE_ROOT_ID
	} else {
		nId = c.inodeMap.Handle(&node.handled)
	}

	if nId == 0 {
		return fuse.OK
	}
	return c.server.InodeNotify(nId, off, length)
}

// EntryNotify makes the kernel forget the entry data from the given
// name from a directory.  After this call, the kernel will issue a
// new lookup request for the given name when necessary. No filesystem
// related locks should be held when calling this.
func (c *FileSystemConnector) EntryNotify(node *Inode, name string) fuse.Status {
	var nId uint64
	if node == c.rootNode {
		nId = fuse.FUSE_ROOT_ID
	} else {
		nId = c.inodeMap.Handle(&node.handled)
	}

	if nId == 0 {
		return fuse.OK
	}
	return c.server.EntryNotify(nId, name)
}

// DeleteNotify signals to the kernel that the named entry in dir for
// the child disappeared. No filesystem related locks should be held
// when calling this.
func (c *FileSystemConnector) DeleteNotify(dir *Inode, child *Inode, name string) fuse.Status {
	var nId uint64

	if dir == c.rootNode {
		nId = fuse.FUSE_ROOT_ID
	} else {
		nId = c.inodeMap.Handle(&dir.handled)
	}

	if nId == 0 {
		return fuse.OK
	}

	chId := c.inodeMap.Handle(&child.handled)

	return c.server.DeleteNotify(nId, chId, name)
}
<|endoftext|>


<commit_before>// Package endpoints provides lookups for all AWS service endpoints.
package endpoints

import (
	"strings"
)

// Lookup returns the endpoint for the given service in the given region plus
// any overrides for the service name and region.
func Lookup(service, region string) (uri, newService, newRegion string) {
	switch service {

	case "cloudfront":

		if !strings.HasPrefix(region, "cn-") {
			return format("https://cloudfront.amazonaws.com", service, region), service, "us-east-1"
		}

	case "dynamodb":

		if region == "local" {
			return format("http://localhost:8000", service, region), "dynamodb", "us-east-1"
		}

	case "elasticmapreduce":

		if strings.HasPrefix(region, "cn-") {
			return format("https://elasticmapreduce.cn-north-1.amazonaws.com.cn", service, region), service, region
		}

		if region == "eu-central-1" {
			return format("https://elasticmapreduce.eu-central-1.amazonaws.com", service, region), service, region
		}

		if region == "us-east-1" {
			return format("https://elasticmapreduce.us-east-1.amazonaws.com", service, region), service, region
		}

		if region != "" {
			return format("https://{region}.elasticmapreduce.amazonaws.com", service, region), service, region
		}

	case "iam":

		if strings.HasPrefix(region, "cn-") {
			return format("https://{service}.cn-north-1.amazonaws.com.cn", service, region), service, region
		}

		if strings.HasPrefix(region, "us-gov") {
			return format("https://{service}.us-gov.amazonaws.com", service, region), service, region
		}

		return format("https://iam.amazonaws.com", service, region), service, "us-east-1"

	case "importexport":

		if !strings.HasPrefix(region, "cn-") {
			return format("https://importexport.amazonaws.com", service, region), service, region
		}

	case "rds":

		if region == "us-east-1" {
			return format("https://rds.amazonaws.com", service, region), service, region
		}

	case "route53":

		if !strings.HasPrefix(region, "cn-") {
			return format("https://route53.amazonaws.com", service, region), service, region
		}

	case "s3":

		if region == "us-east-1" || region == "" {
			return format("{scheme}://s3.amazonaws.com", service, region), service, "us-east-1"
		}

		if strings.HasPrefix(region, "cn-") {
			return format("{scheme}://{service}.{region}.amazonaws.com.cn", service, region), service, region
		}

		if region == "us-east-1" || region == "ap-northeast-1" || region == "sa-east-1" || region == "ap-southeast-1" || region == "ap-southeast-2" || region == "us-west-2" || region == "us-west-1" || region == "eu-west-1" || region == "us-gov-west-1" || region == "fips-us-gov-west-1" {
			return format("{scheme}://{service}-{region}.amazonaws.com", service, region), service, region
		}

		if region != "" {
			return format("{scheme}://{service}.{region}.amazonaws.com", service, region), service, region
		}

	case "sdb":

		if region == "us-east-1" {
			return format("https://sdb.amazonaws.com", service, region), service, region
		}

	case "sqs":

		if region == "us-east-1" {
			return format("https://queue.amazonaws.com", service, region), service, region
		}

		if strings.HasPrefix(region, "cn-") {
			return format("https://{region}.queue.amazonaws.com.cn", service, region), service, region
		}

		if region != "" {
			return format("https://{region}.queue.amazonaws.com", service, region), service, region
		}

	case "sts":

		if strings.HasPrefix(region, "cn-") {
			return format("{scheme}://{service}.cn-north-1.amazonaws.com.cn", service, region), service, region
		}

		if strings.HasPrefix(region, "us-gov") {
			return format("https://{service}.{region}.amazonaws.com", service, region), service, region
		}

		return format("https://sts.amazonaws.com", service, region), service, "us-east-1"

	}

	if strings.HasPrefix(region, "cn-") {
		return format("{scheme}://{service}.{region}.amazonaws.com.cn", service, region), service, region
	}

	if region != "" {
		return format("{scheme}://{service}.{region}.amazonaws.com", service, region), service, region
	}

	panic("unknown endpoint for " + service + " in " + region)
}

func format(uri, service, region string) string {
	uri = strings.Replace(uri, "{scheme}", "https", -1)
	uri = strings.Replace(uri, "{service}", service, -1)
	uri = strings.Replace(uri, "{region}", region, -1)
	return uri
}
<commit_msg>Freshen clients.<commit_after>// THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT.

// Package endpoints provides lookups for all AWS service endpoints.
package endpoints

import (
	"strings"
)

// Lookup returns the endpoint for the given service in the given region plus
// any overrides for the service name and region.
func Lookup(service, region string) (uri, newService, newRegion string) {
	switch service {

	case "cloudfront":

		if !strings.HasPrefix(region, "cn-") {
			return format("https://cloudfront.amazonaws.com", service, region), service, "us-east-1"
		}

	case "dynamodb":

		if region == "local" {
			return format("http://localhost:8000", service, region), "dynamodb", "us-east-1"
		}

	case "elasticmapreduce":

		if strings.HasPrefix(region, "cn-") {
			return format("https://elasticmapreduce.cn-north-1.amazonaws.com.cn", service, region), service, region
		}

		if region == "eu-central-1" {
			return format("https://elasticmapreduce.eu-central-1.amazonaws.com", service, region), service, region
		}

		if region == "us-east-1" {
			return format("https://elasticmapreduce.us-east-1.amazonaws.com", service, region), service, region
		}

		if region != "" {
			return format("https://{region}.elasticmapreduce.amazonaws.com", service, region), service, region
		}

	case "iam":

		if strings.HasPrefix(region, "cn-") {
			return format("https://{service}.cn-north-1.amazonaws.com.cn", service, region), service, region
		}

		if strings.HasPrefix(region, "us-gov") {
			return format("https://{service}.us-gov.amazonaws.com", service, region), service, region
		}

		return format("https://iam.amazonaws.com", service, region), service, "us-east-1"

	case "importexport":

		if !strings.HasPrefix(region, "cn-") {
			return format("https://importexport.amazonaws.com", service, region), service, region
		}

	case "rds":

		if region == "us-east-1" {
			return format("https://rds.amazonaws.com", service, region), service, region
		}

	case "route53":

		if !strings.HasPrefix(region, "cn-") {
			return format("https://route53.amazonaws.com", service, region), service, region
		}

	case "s3":

		if region == "us-east-1" || region == "" {
			return format("{scheme}://s3.amazonaws.com", service, region), service, "us-east-1"
		}

		if strings.HasPrefix(region, "cn-") {
			return format("{scheme}://{service}.{region}.amazonaws.com.cn", service, region), service, region
		}

		if region == "us-east-1" || region == "ap-northeast-1" || region == "sa-east-1" || region == "ap-southeast-1" || region == "ap-southeast-2" || region == "us-west-2" || region == "us-west-1" || region == "eu-west-1" || region == "us-gov-west-1" || region == "fips-us-gov-west-1" {
			return format("{scheme}://{service}-{region}.amazonaws.com", service, region), service, region
		}

		if region != "" {
			return format("{scheme}://{service}.{region}.amazonaws.com", service, region), service, region
		}

	case "sdb":

		if region == "us-east-1" {
			return format("https://sdb.amazonaws.com", service, region), service, region
		}

	case "sqs":

		if region == "us-east-1" {
			return format("https://queue.amazonaws.com", service, region), service, region
		}

		if strings.HasPrefix(region, "cn-") {
			return format("https://{region}.queue.amazonaws.com.cn", service, region), service, region
		}

		if region != "" {
			return format("https://{region}.queue.amazonaws.com", service, region), service, region
		}

	case "sts":

		if strings.HasPrefix(region, "cn-") {
			return format("{scheme}://{service}.cn-north-1.amazonaws.com.cn", service, region), service, region
		}

		if strings.HasPrefix(region, "us-gov") {
			return format("https://{service}.{region}.amazonaws.com", service, region), service, region
		}

		return format("https://sts.amazonaws.com", service, region), service, "us-east-1"

	}

	if strings.HasPrefix(region, "cn-") {
		return format("{scheme}://{service}.{region}.amazonaws.com.cn", service, region), service, region
	}

	if region != "" {
		return format("{scheme}://{service}.{region}.amazonaws.com", service, region), service, region
	}

	panic("unknown endpoint for " + service + " in " + region)
}

func format(uri, service, region string) string {
	uri = strings.Replace(uri, "{scheme}", "https", -1)
	uri = strings.Replace(uri, "{service}", service, -1)
	uri = strings.Replace(uri, "{region}", region, -1)
	return uri
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"os"

	"github.com/jsgoecke/tesla"
)

func main() {
	client, err := tesla.NewClient(
		&tesla.Auth{
			ClientID:     os.Getenv("TESLA_CLIENT_ID"),
			ClientSecret: os.Getenv("TESLA_CLIENT_SECRET"),
			Email:        os.Getenv("TESLA_USERNAME"),
			Password:     os.Getenv("TESLA_PASSWORD"),
		})
	if err != nil {
		panic(err)
	}

	vehicles, err := client.Vehicles()
	if err != nil {
		panic(err)
	}
	vehicle := vehicles[0]
	status, err := vehicle.MobileEnabled()

	if err != nil {
		panic(err)
	}

	fmt.Println(status)
	fmt.Println(vehicle.ChargeState())
	fmt.Println(vehicle.ClimateState())
	fmt.Println(vehicle.DriveState())
	fmt.Println(vehicle.GuiSettings())
	fmt.Println(vehicle.VehicleState())
	fmt.Println(vehicle.HonkHorn())
	fmt.Println(vehicle.FlashLights())
	fmt.Println(vehicle.Wakeup())
	fmt.Println(vehicle.OpenChargePort())
	fmt.Println(vehicle.ResetValetPIN())
	fmt.Println(vehicle.SetChargeLimitStandard())
	fmt.Println(vehicle.SetChargeLimit(50))
	fmt.Println(vehicle.StartCharging())
	fmt.Println(vehicle.StopCharging())
	fmt.Println(vehicle.SetChargeLimitMax())
	fmt.Println(vehicle.StartAirConditioning())
	fmt.Println(vehicle.StopAirConditioning())
	fmt.Println(vehicle.UnlockDoors())
	fmt.Println(vehicle.LockDoors())
	fmt.Println(vehicle.SetTemprature(72.0, 72.0))
	fmt.Println(vehicle.Start(os.Getenv("TESLA_PASSWORD")))
	fmt.Println(vehicle.OpenTrunk("rear"))
	fmt.Println(vehicle.OpenTrunk("front"))
	fmt.Println(vehicle.MovePanoRoof("vent", 0))
	fmt.Println(vehicle.MovePanoRoof("open", 0))
	fmt.Println(vehicle.MovePanoRoof("move", 50))
	fmt.Println(vehicle.MovePanoRoof("close", 0))
	fmt.Println(vehicle.TriggerHomelink())

	// Take care with these, as the car will move
	fmt.Println(vehicle.AutoparkForward())
	fmt.Println(vehicle.AutoparkReverse())
	// Take care with these, as the car will move

	// Stream vehicle events
	eventChan, errChan, err := vehicle.Stream()
	if err != nil {
		fmt.Println(err)
		return
	} else {
		for {
			select {
			case event := <-eventChan:
				eventJSON, _ := json.Marshal(event)
				fmt.Println(string(eventJSON))
			case err = <-errChan:
				fmt.Println(err)
				if err.Error() == "HTTP stream closed" {
					fmt.Println("Reconnecting!")
					eventChan, errChan, err := vehicle.Stream()
					if err != nil {
						fmt.Println(err)
						return
					}
				}
			}
		}
	}
}
<commit_msg>Updated the README<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"os"

	"github.com/jsgoecke/tesla"
)

func main() {
	client, err := tesla.NewClient(
		&tesla.Auth{
			ClientID:     os.Getenv("TESLA_CLIENT_ID"),
			ClientSecret: os.Getenv("TESLA_CLIENT_SECRET"),
			Email:        os.Getenv("TESLA_USERNAME"),
			Password:     os.Getenv("TESLA_PASSWORD"),
		})
	if err != nil {
		panic(err)
	}

	vehicles, err := client.Vehicles()
	if err != nil {
		panic(err)
	}
	vehicle := vehicles[0]
	status, err := vehicle.MobileEnabled()

	if err != nil {
		panic(err)
	}

	fmt.Println(status)
	fmt.Println(vehicle.ChargeState())
	fmt.Println(vehicle.ClimateState())
	fmt.Println(vehicle.DriveState())
	fmt.Println(vehicle.GuiSettings())
	fmt.Println(vehicle.VehicleState())
	fmt.Println(vehicle.HonkHorn())
	fmt.Println(vehicle.FlashLights())
	fmt.Println(vehicle.Wakeup())
	fmt.Println(vehicle.OpenChargePort())
	fmt.Println(vehicle.ResetValetPIN())
	fmt.Println(vehicle.SetChargeLimitStandard())
	fmt.Println(vehicle.SetChargeLimit(50))
	fmt.Println(vehicle.StartCharging())
	fmt.Println(vehicle.StopCharging())
	fmt.Println(vehicle.SetChargeLimitMax())
	fmt.Println(vehicle.StartAirConditioning())
	fmt.Println(vehicle.StopAirConditioning())
	fmt.Println(vehicle.UnlockDoors())
	fmt.Println(vehicle.LockDoors())
	fmt.Println(vehicle.SetTemprature(72.0, 72.0))
	fmt.Println(vehicle.Start(os.Getenv("TESLA_PASSWORD")))
	fmt.Println(vehicle.OpenTrunk("rear"))
	fmt.Println(vehicle.OpenTrunk("front"))
	fmt.Println(vehicle.MovePanoRoof("vent", 0))
	fmt.Println(vehicle.MovePanoRoof("open", 0))
	fmt.Println(vehicle.MovePanoRoof("move", 50))
	fmt.Println(vehicle.MovePanoRoof("close", 0))
	fmt.Println(vehicle.TriggerHomelink())

	// // Take care with these, as the car will move
	fmt.Println(vehicle.AutoparkForward())
	fmt.Println(vehicle.AutoparkReverse())
	// Take care with these, as the car will move

	// Stream vehicle events
	eventChan, errChan, err := vehicle.Stream()
	if err != nil {
		fmt.Println(err)
		return
	} else {
		for {
			select {
			case event := <-eventChan:
				eventJSON, _ := json.Marshal(event)
				fmt.Println(string(eventJSON))
			case err = <-errChan:
				fmt.Println(err)
<<<<<<< HEAD
				if err.Error() == "HTTP stream closed" {
					fmt.Println("Reconnecting!")
					eventChan, errChan, err := vehicle.Stream()
					if err != nil {
						fmt.Println(err)
						return
					}
=======
				fmt.Println("Reconnecting!")
				eventChan, errChan, err = vehicle.Stream()
				if err != nil {
					break
>>>>>>> master
				}
			}
		}
	}
}
<|endoftext|>


<commit_before>package cache

import (
	"testing"
)

const (
	ns1  = "namespace1"
	ns2  = "namespace2"
	key1 = "key1"
	key2 = "key2"
	val1 = "val1"
	val2 = "val2"
)

func TestMultipleNamespaces(t *testing.T) {
	c := GetInstance()

	got1, err := c.LoadOrStore(ns1, key1, func() (interface{}, error) { return val1, nil })
	if err != nil {
		t.Errorf("LoadOrStore(%v,%v) returned error: %v", ns1, key1, err)
	}
	if got1 != val1 {
		t.Errorf("LoadOrStore(%v,%v) loaded wrong value: got %v, want %v", ns1, key1, got1, val1)
	}
	got2, err := c.LoadOrStore(ns2, key1, func() (interface{}, error) { return val2, nil })
	if err != nil {
		t.Errorf("LoadOrStore(%v,%v) returned error: %v", ns2, key1, err)
	}
	if got2 != val2 {
		t.Errorf("LoadOrStore(%v,%v) loaded wrong value: got %v, want %v", ns2, key1, got2, val2)
	}
	got3, err := c.LoadOrStore(ns1, key1, func() (interface{}, error) { return val1, nil })
	if err != nil {
		t.Errorf("LoadOrStore(%v,%v) returned error: %v", ns1, key1, err)
	}
	if got3 != val1 {
		t.Errorf("LoadOrStore(%v,%v) loaded wrong value: got %v, want %v", ns1, key1, got3, val1)
	}
}

func TestDelete(t *testing.T) {
	c := GetInstance()

	got1, err := c.LoadOrStore(ns1, key1, func() (interface{}, error) { return val1, nil })
	if err != nil {
		t.Fatalf("LoadOrStore(%v,%v) returned error: %v", ns1, key1, err)
	}
	if got1 != val1 {
		t.Fatalf("LoadOrStore(%v,%v) loaded wrong value: got %v, want %v", ns1, key1, got1, val1)
	}
	got2, err := c.LoadOrStore(ns2, key1, func() (interface{}, error) { return val2, nil })
	if err != nil {
		t.Fatalf("LoadOrStore(%v,%v) returned error: %v", ns2, key1, err)
	}
	if got2 != val2 {
		t.Fatalf("LoadOrStore(%v,%v) loaded wrong value: got %v, want %v", ns2, key1, got2, val2)
	}

	err = c.Delete(ns1, key1)
	if err != nil {
		t.Errorf("Delete(%v,%v) returned error: %v", ns1, key1, err)
	}

	got1, err = c.LoadOrStore(ns1, key1, func() (interface{}, error) { return val2, nil })
	if err != nil {
		t.Errorf("LoadOrStore(%v,%v) returned error: %v", ns1, key1, err)
	}
	if got1 != val2 {
		t.Errorf("LoadOrStore(%v,%v) loaded wrong value: got %v, want %v", ns1, key1, got1, val2)
	}
	got2, err = c.LoadOrStore(ns2, key1, func() (interface{}, error) { return val1, nil })
	if err != nil {
		t.Errorf("LoadOrStore(%v,%v) returned error: %v", ns2, key1, err)
	}
	if got2 != val2 {
		t.Errorf("LoadOrStore(%v,%v) loaded wrong value: got %v, want %v", ns2, key1, got2, val2)
	}
}
<commit_msg>Fixes #151. Make sure to reset the singleton cache after every test case. (#152)<commit_after>package cache

import (
	"testing"
)

const (
	ns1  = "namespace1"
	ns2  = "namespace2"
	key1 = "key1"
	key2 = "key2"
	val1 = "val1"
	val2 = "val2"
)

func TestMultipleNamespaces(t *testing.T) {
	c := GetInstance()
	defer c.Reset()

	got1, err := c.LoadOrStore(ns1, key1, func() (interface{}, error) { return val1, nil })
	if err != nil {
		t.Errorf("LoadOrStore(%v,%v) returned error: %v", ns1, key1, err)
	}
	if got1 != val1 {
		t.Errorf("LoadOrStore(%v,%v) loaded wrong value: got %v, want %v", ns1, key1, got1, val1)
	}
	got2, err := c.LoadOrStore(ns2, key1, func() (interface{}, error) { return val2, nil })
	if err != nil {
		t.Errorf("LoadOrStore(%v,%v) returned error: %v", ns2, key1, err)
	}
	if got2 != val2 {
		t.Errorf("LoadOrStore(%v,%v) loaded wrong value: got %v, want %v", ns2, key1, got2, val2)
	}
	got3, err := c.LoadOrStore(ns1, key1, func() (interface{}, error) { return val1, nil })
	if err != nil {
		t.Errorf("LoadOrStore(%v,%v) returned error: %v", ns1, key1, err)
	}
	if got3 != val1 {
		t.Errorf("LoadOrStore(%v,%v) loaded wrong value: got %v, want %v", ns1, key1, got3, val1)
	}
}

func TestDelete(t *testing.T) {
	c := GetInstance()
	defer c.Reset()

	got1, err := c.LoadOrStore(ns1, key1, func() (interface{}, error) { return val1, nil })
	if err != nil {
		t.Fatalf("LoadOrStore(%v,%v) returned error: %v", ns1, key1, err)
	}
	if got1 != val1 {
		t.Fatalf("LoadOrStore(%v,%v) loaded wrong value: got %v, want %v", ns1, key1, got1, val1)
	}
	got2, err := c.LoadOrStore(ns2, key1, func() (interface{}, error) { return val2, nil })
	if err != nil {
		t.Fatalf("LoadOrStore(%v,%v) returned error: %v", ns2, key1, err)
	}
	if got2 != val2 {
		t.Fatalf("LoadOrStore(%v,%v) loaded wrong value: got %v, want %v", ns2, key1, got2, val2)
	}

	err = c.Delete(ns1, key1)
	if err != nil {
		t.Errorf("Delete(%v,%v) returned error: %v", ns1, key1, err)
	}

	got1, err = c.LoadOrStore(ns1, key1, func() (interface{}, error) { return val2, nil })
	if err != nil {
		t.Errorf("LoadOrStore(%v,%v) returned error: %v", ns1, key1, err)
	}
	if got1 != val2 {
		t.Errorf("LoadOrStore(%v,%v) loaded wrong value: got %v, want %v", ns1, key1, got1, val2)
	}
	got2, err = c.LoadOrStore(ns2, key1, func() (interface{}, error) { return val1, nil })
	if err != nil {
		t.Errorf("LoadOrStore(%v,%v) returned error: %v", ns2, key1, err)
	}
	if got2 != val2 {
		t.Errorf("LoadOrStore(%v,%v) loaded wrong value: got %v, want %v", ns2, key1, got2, val2)
	}
}
<|endoftext|>


<commit_before>// Copyright 2017 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

package service

import (
	"encoding/json"
	"fmt"

	"golang.org/x/net/context"

	"github.com/keybase/client/go/gregor"
	"github.com/keybase/client/go/libkb"
	"github.com/keybase/client/go/protocol/gregor1"
	"github.com/keybase/client/go/protocol/keybase1"
	"github.com/keybase/client/go/teams"
)

const teamHandlerName = "teamHandler"

type teamHandler struct {
	libkb.Contextified
}

func newTeamHandler(g *libkb.GlobalContext) *teamHandler {
	return &teamHandler{
		Contextified: libkb.NewContextified(g),
	}
}

func (r *teamHandler) Create(ctx context.Context, cli gregor1.IncomingInterface, category string, item gregor.Item) (bool, error) {
	switch category {
	case "team.clkr":
		return true, r.rotateTeam(ctx, item)
	case "team.sbs":
		return true, r.sharingBeforeSignup(ctx, item)
	default:
		return false, fmt.Errorf("unknown teamHandler category: %q", category)
	}
}

func (r *teamHandler) rotateTeam(ctx context.Context, item gregor.Item) error {
	r.G().Log.Debug("team.clkr received")
	var msg keybase1.TeamCLKRMsg
	if err := json.Unmarshal(item.Body().Bytes(), &msg); err != nil {
		r.G().Log.Debug("error unmarshaling team.clkr item: %s", err)
		return err
	}
	r.G().Log.Debug("team.clkr unmarshaled: %+v", msg)

	return teams.HandleRotateTeamRequest(ctx, r.G(), msg.TeamID, teams.PerTeamSecretGeneration(msg.Generation))
}

func (r *teamHandler) sharingBeforeSignup(ctx context.Context, item gregor.Item) error {
	r.G().Log.Debug("team.sbs (sharing before signup) not yet implemented")
	return nil
}

func (r *teamHandler) Dismiss(ctx context.Context, cli gregor1.IncomingInterface, category string, item gregor.Item) (bool, error) {
	return false, nil
}

func (r *teamHandler) IsAlive() bool {
	return true
}

func (r *teamHandler) Name() string {
	return teamHandlerName
}
<commit_msg>Rename<commit_after>// Copyright 2017 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

package service

import (
	"encoding/json"
	"fmt"

	"golang.org/x/net/context"

	"github.com/keybase/client/go/gregor"
	"github.com/keybase/client/go/libkb"
	"github.com/keybase/client/go/protocol/gregor1"
	"github.com/keybase/client/go/protocol/keybase1"
	"github.com/keybase/client/go/teams"
)

const teamHandlerName = "teamHandler"

type teamHandler struct {
	libkb.Contextified
}

func newTeamHandler(g *libkb.GlobalContext) *teamHandler {
	return &teamHandler{
		Contextified: libkb.NewContextified(g),
	}
}

func (r *teamHandler) Create(ctx context.Context, cli gregor1.IncomingInterface, category string, item gregor.Item) (bool, error) {
	switch category {
	case "team.clkr":
		return true, r.rotateTeam(ctx, item)
	case "team.sbs":
		return true, r.sharingBeforeSignup(ctx, item)
	default:
		return false, fmt.Errorf("unknown teamHandler category: %q", category)
	}
}

func (r *teamHandler) rotateTeam(ctx context.Context, item gregor.Item) error {
	r.G().Log.Debug("team.clkr received")
	var msg keybase1.TeamCLKRMsg
	if err := json.Unmarshal(item.Body().Bytes(), &msg); err != nil {
		r.G().Log.Debug("error unmarshaling team.clkr item: %s", err)
		return err
	}
	r.G().Log.Debug("team.clkr unmarshaled: %+v", msg)

	return teams.HandleRotateRequest(ctx, r.G(), msg.TeamID, teams.PerTeamSecretGeneration(msg.Generation))
}

func (r *teamHandler) sharingBeforeSignup(ctx context.Context, item gregor.Item) error {
	r.G().Log.Debug("team.sbs (sharing before signup) not yet implemented")
	return nil
}

func (r *teamHandler) Dismiss(ctx context.Context, cli gregor1.IncomingInterface, category string, item gregor.Item) (bool, error) {
	return false, nil
}

func (r *teamHandler) IsAlive() bool {
	return true
}

func (r *teamHandler) Name() string {
	return teamHandlerName
}
<|endoftext|>


<commit_before>package flying_test

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"syscall"

	"github.com/concourse/testflight/helpers"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gbytes"
	"github.com/onsi/gomega/gexec"
)

var _ = Describe("Flying", func() {
	var tmpdir string
	var fixture string

	BeforeEach(func() {
		var err error

		tmpdir, err = ioutil.TempDir("", "fly-test")
		Expect(err).NotTo(HaveOccurred())

		fixture = filepath.Join(tmpdir, "fixture")

		err = os.MkdirAll(fixture, 0755)
		Expect(err).NotTo(HaveOccurred())

		err = ioutil.WriteFile(
			filepath.Join(fixture, "run"),
			[]byte(`#!/bin/sh
echo some output
echo FOO is $FOO
echo ARGS are "$@"
exit 0
`),
			0755,
		)
		Expect(err).NotTo(HaveOccurred())

		err = ioutil.WriteFile(
			filepath.Join(fixture, "task.yml"),
			[]byte(`---
platform: linux

image_resource:
  type: docker-image
  source: {repository: busybox}

inputs:
- name: fixture

outputs:
- name: output-1
- name: output-2

params:
  FOO: 1

run:
  path: fixture/run
`),
			0644,
		)
		Expect(err).NotTo(HaveOccurred())
	})

	AfterEach(func() {
		os.RemoveAll(tmpdir)
	})

	It("works", func() {
		fly := exec.Command(flyBin, "-t", targetedConcourse, "execute", "-c", "task.yml", "--", "SOME", "ARGS")
		fly.Dir = fixture

		session := helpers.StartFly(fly)

		Eventually(session).Should(gexec.Exit(0))

		Expect(session).To(gbytes.Say("some output"))
		Expect(session).To(gbytes.Say("FOO is 1"))
		Expect(session).To(gbytes.Say("ARGS are SOME ARGS"))
	})

	Describe("hijacking", func() {
		It("executes an interactive command in a running task's container", func() {
			err := ioutil.WriteFile(
				filepath.Join(fixture, "run"),
				[]byte(`#!/bin/sh
mkfifo /tmp/fifo
echo waiting
cat < /tmp/fifo
`),
				0755,
			)
			Expect(err).NotTo(HaveOccurred())

			fly := exec.Command(flyBin, "-t", targetedConcourse, "execute", "-c", "task.yml")
			fly.Dir = fixture

			flyS := helpers.StartFly(fly)

			Eventually(flyS).Should(gbytes.Say("executing build"))

			buildRegex := regexp.MustCompile(`executing build (\d+)`)
			matches := buildRegex.FindSubmatch(flyS.Out.Contents())
			buildID := string(matches[1])

			Eventually(flyS).Should(gbytes.Say("waiting"))

			hijack := exec.Command(flyBin, "-t", targetedConcourse, "hijack", "-b", buildID, "-s", "one-off", "--", "sh", "-c", "echo marco > /tmp/fifo")

			hijackIn, err := hijack.StdinPipe()
			Expect(err).NotTo(HaveOccurred())

			hijackS := helpers.StartFly(hijack)

			Eventually(hijackS).Should(gbytes.Say("3: .+ type: task"))
			fmt.Fprintln(hijackIn, "3")

			Eventually(flyS).Should(gbytes.Say("marco"))

			Eventually(hijackS).Should(gexec.Exit())

			Eventually(flyS).Should(gexec.Exit(0))
		})
	})

	Describe("pulling down outputs", func() {
		It("works", func() {
			err := ioutil.WriteFile(
				filepath.Join(fixture, "run"),
				[]byte(`#!/bin/sh
echo hello > output-1/file-1
echo world > output-2/file-2
`),
				0755,
			)
			Expect(err).NotTo(HaveOccurred())

			fly := exec.Command(flyBin, "-t", targetedConcourse, "execute", "-c", "task.yml", "-o", "output-1=./output-1", "-o", "output-2=./output-2")
			fly.Dir = fixture

			session := helpers.StartFly(fly)
			<-session.Exited

			Expect(session.ExitCode()).To(Equal(0))

			file1 := filepath.Join(fixture, "output-1", "file-1")
			file2 := filepath.Join(fixture, "output-2", "file-2")

			Expect(ioutil.ReadFile(file1)).To(Equal([]byte("hello\n")))
			Expect(ioutil.ReadFile(file2)).To(Equal([]byte("world\n")))
		})
	})

	Describe("aborting", func() {
		It("terminates the running task", func() {
			err := ioutil.WriteFile(
				filepath.Join(fixture, "run"),
				[]byte(`#!/bin/sh
sleep 1000 &
echo waiting
wait
`),
				0755,
			)
			Expect(err).NotTo(HaveOccurred())

			fly := exec.Command(flyBin, "-t", targetedConcourse, "execute", "-c", "task.yml")
			fly.Dir = fixture

			flyS := helpers.StartFly(fly)

			Eventually(flyS).Should(gbytes.Say("waiting"))

			flyS.Signal(syscall.SIGTERM)

			// build should have been aborted
			Eventually(flyS).Should(gexec.Exit(3))
		})
	})
})
<commit_msg>Revert "don't expect aborted task to get sigterm"<commit_after>package flying_test

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"syscall"

	"github.com/concourse/testflight/helpers"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gbytes"
	"github.com/onsi/gomega/gexec"
)

var _ = Describe("Flying", func() {
	var tmpdir string
	var fixture string

	BeforeEach(func() {
		var err error

		tmpdir, err = ioutil.TempDir("", "fly-test")
		Expect(err).NotTo(HaveOccurred())

		fixture = filepath.Join(tmpdir, "fixture")

		err = os.MkdirAll(fixture, 0755)
		Expect(err).NotTo(HaveOccurred())

		err = ioutil.WriteFile(
			filepath.Join(fixture, "run"),
			[]byte(`#!/bin/sh
echo some output
echo FOO is $FOO
echo ARGS are "$@"
exit 0
`),
			0755,
		)
		Expect(err).NotTo(HaveOccurred())

		err = ioutil.WriteFile(
			filepath.Join(fixture, "task.yml"),
			[]byte(`---
platform: linux

image_resource:
  type: docker-image
  source: {repository: busybox}

inputs:
- name: fixture

outputs:
- name: output-1
- name: output-2

params:
  FOO: 1

run:
  path: fixture/run
`),
			0644,
		)
		Expect(err).NotTo(HaveOccurred())
	})

	AfterEach(func() {
		os.RemoveAll(tmpdir)
	})

	It("works", func() {
		fly := exec.Command(flyBin, "-t", targetedConcourse, "execute", "-c", "task.yml", "--", "SOME", "ARGS")
		fly.Dir = fixture

		session := helpers.StartFly(fly)

		Eventually(session).Should(gexec.Exit(0))

		Expect(session).To(gbytes.Say("some output"))
		Expect(session).To(gbytes.Say("FOO is 1"))
		Expect(session).To(gbytes.Say("ARGS are SOME ARGS"))
	})

	Describe("hijacking", func() {
		It("executes an interactive command in a running task's container", func() {
			err := ioutil.WriteFile(
				filepath.Join(fixture, "run"),
				[]byte(`#!/bin/sh
mkfifo /tmp/fifo
echo waiting
cat < /tmp/fifo
`),
				0755,
			)
			Expect(err).NotTo(HaveOccurred())

			fly := exec.Command(flyBin, "-t", targetedConcourse, "execute", "-c", "task.yml")
			fly.Dir = fixture

			flyS := helpers.StartFly(fly)

			Eventually(flyS).Should(gbytes.Say("executing build"))

			buildRegex := regexp.MustCompile(`executing build (\d+)`)
			matches := buildRegex.FindSubmatch(flyS.Out.Contents())
			buildID := string(matches[1])

			Eventually(flyS).Should(gbytes.Say("waiting"))

			hijack := exec.Command(flyBin, "-t", targetedConcourse, "hijack", "-b", buildID, "-s", "one-off", "--", "sh", "-c", "echo marco > /tmp/fifo")

			hijackIn, err := hijack.StdinPipe()
			Expect(err).NotTo(HaveOccurred())

			hijackS := helpers.StartFly(hijack)

			Eventually(hijackS).Should(gbytes.Say("3: .+ type: task"))
			fmt.Fprintln(hijackIn, "3")

			Eventually(flyS).Should(gbytes.Say("marco"))

			Eventually(hijackS).Should(gexec.Exit())

			Eventually(flyS).Should(gexec.Exit(0))
		})
	})

	Describe("pulling down outputs", func() {
		It("works", func() {
			err := ioutil.WriteFile(
				filepath.Join(fixture, "run"),
				[]byte(`#!/bin/sh
echo hello > output-1/file-1
echo world > output-2/file-2
`),
				0755,
			)
			Expect(err).NotTo(HaveOccurred())

			fly := exec.Command(flyBin, "-t", targetedConcourse, "execute", "-c", "task.yml", "-o", "output-1=./output-1", "-o", "output-2=./output-2")
			fly.Dir = fixture

			session := helpers.StartFly(fly)
			<-session.Exited

			Expect(session.ExitCode()).To(Equal(0))

			file1 := filepath.Join(fixture, "output-1", "file-1")
			file2 := filepath.Join(fixture, "output-2", "file-2")

			Expect(ioutil.ReadFile(file1)).To(Equal([]byte("hello\n")))
			Expect(ioutil.ReadFile(file2)).To(Equal([]byte("world\n")))
		})
	})

	Describe("aborting", func() {
		It("terminates the running task", func() {
			err := ioutil.WriteFile(
				filepath.Join(fixture, "run"),
				[]byte(`#!/bin/sh
trap "echo task got sigterm; exit 1" SIGTERM
sleep 1000 &
echo waiting
wait
`),
				0755,
			)
			Expect(err).NotTo(HaveOccurred())

			fly := exec.Command(flyBin, "-t", targetedConcourse, "execute", "-c", "task.yml")
			fly.Dir = fixture

			flyS := helpers.StartFly(fly)

			Eventually(flyS).Should(gbytes.Say("waiting"))

			flyS.Signal(syscall.SIGTERM)

			Eventually(flyS).Should(gbytes.Say("task got sigterm"))

			// build should have been aborted
			Eventually(flyS).Should(gexec.Exit(3))
		})
	})
})
<|endoftext|>


<commit_before>package ui

import (
	"crypto/md5"
	"encoding/binary"
	"fmt"
	"image"
	"image/color"
	"image/draw"
	"image/gif"
	"image/png"
	"io/ioutil"
	"log"
	"os"
	"os/user"
	"path"

	"github.com/fogleman/nes/nes"
	"github.com/go-gl/gl/v2.1/gl"
	"github.com/go-gl/glfw/v3.1/glfw"
)

var homeDir string

func init() {
	u, err := user.Current()
	if err != nil {
		log.Fatalln(err)
	}
	homeDir = u.HomeDir
}

func thumbnailURL(hash string) string {
	return "http://www.michaelfogleman.com/static/nes/" + hash + ".png"
}

func thumbnailPath(hash string) string {
	return homeDir + "/.nes/thumbnail/" + hash + ".png"
}

func sramPath(hash string) string {
	return homeDir + "/.nes/sram/" + hash + ".dat"
}

func savePath(hash string) string {
	return homeDir + "/.nes/save/" + hash + ".dat"
}

func readKey(window *glfw.Window, key glfw.Key) bool {
	return window.GetKey(key) == glfw.Press
}

func readKeys(window *glfw.Window, turbo bool) [8]bool {
	var result [8]bool
	result[nes.ButtonA] = readKey(window, glfw.KeyZ) || (turbo && readKey(window, glfw.KeyA))
	result[nes.ButtonB] = readKey(window, glfw.KeyX) || (turbo && readKey(window, glfw.KeyS))
	result[nes.ButtonSelect] = readKey(window, glfw.KeyRightShift)
	result[nes.ButtonStart] = readKey(window, glfw.KeyEnter)
	result[nes.ButtonUp] = readKey(window, glfw.KeyUp)
	result[nes.ButtonDown] = readKey(window, glfw.KeyDown)
	result[nes.ButtonLeft] = readKey(window, glfw.KeyLeft)
	result[nes.ButtonRight] = readKey(window, glfw.KeyRight)
	return result
}

func readJoystick(joy glfw.Joystick, turbo bool) [8]bool {
	var result [8]bool
	if !glfw.JoystickPresent(joy) {
		return result
	}
	joyname := glfw.GetJoystickName(joy)
	axes := glfw.GetJoystickAxes(joy)
	buttons := glfw.GetJoystickButtons(joy)
	if joyname == "PLAYSTATION(R)3 Controller" {
		result[nes.ButtonA] = buttons[14] == 1 || (turbo && buttons[2] == 1)
		result[nes.ButtonB] = buttons[13] == 1 || (turbo && buttons[3] == 1)
		result[nes.ButtonSelect] = buttons[0] == 1
		result[nes.ButtonStart] = buttons[3] == 1
		result[nes.ButtonUp] =  buttons[4] == 1 || axes[1] < -0.5
		result[nes.ButtonDown] = buttons[6] == 1 || axes[1] > 0.5
		result[nes.ButtonLeft] = buttons[7] == 1 || axes[0] < -0.5
		result[nes.ButtonRight] = buttons[5] == 1 || axes[0] > 0.5
		return result
	}
	result[nes.ButtonA] = buttons[0] == 1 || (turbo && buttons[2] == 1)
	result[nes.ButtonB] = buttons[1] == 1 || (turbo && buttons[3] == 1)
	result[nes.ButtonSelect] = buttons[6] == 1
	result[nes.ButtonStart] = buttons[7] == 1
	result[nes.ButtonUp] = axes[1] < -0.5
	result[nes.ButtonDown] = axes[1] > 0.5
	result[nes.ButtonLeft] = axes[0] < -0.5
	result[nes.ButtonRight] = axes[0] > 0.5
	return result
}

func joystickReset(joy glfw.Joystick) bool {
	if !glfw.JoystickPresent(joy) {
		return false
	}
	buttons := glfw.GetJoystickButtons(joy)
	return buttons[4] == 1 && buttons[5] == 1
}

func combineButtons(a, b [8]bool) [8]bool {
	var result [8]bool
	for i := 0; i < 8; i++ {
		result[i] = a[i] || b[i]
	}
	return result
}

func hashFile(path string) (string, error) {
	data, err := ioutil.ReadFile(path)
	if err != nil {
		return "", err
	}
	return fmt.Sprintf("%x", md5.Sum(data)), nil
}

func createTexture() uint32 {
	var texture uint32
	gl.GenTextures(1, &texture)
	gl.BindTexture(gl.TEXTURE_2D, texture)
	gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
	gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
	gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
	gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
	gl.BindTexture(gl.TEXTURE_2D, 0)
	return texture
}

func setTexture(im *image.RGBA) {
	size := im.Rect.Size()
	gl.TexImage2D(
		gl.TEXTURE_2D, 0, gl.RGBA, int32(size.X), int32(size.Y),
		0, gl.RGBA, gl.UNSIGNED_BYTE, gl.Ptr(im.Pix))
}

func copyImage(src image.Image) *image.RGBA {
	dst := image.NewRGBA(src.Bounds())
	draw.Draw(dst, dst.Rect, src, image.ZP, draw.Src)
	return dst
}

func loadPNG(path string) (image.Image, error) {
	file, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer file.Close()
	return png.Decode(file)
}

func savePNG(path string, im image.Image) error {
	file, err := os.Create(path)
	if err != nil {
		return err
	}
	defer file.Close()
	return png.Encode(file, im)
}

func saveGIF(path string, frames []image.Image) error {
	var palette []color.Color
	for _, c := range nes.Palette {
		palette = append(palette, c)
	}
	g := gif.GIF{}
	for i, src := range frames {
		if i%3 != 0 {
			continue
		}
		dst := image.NewPaletted(src.Bounds(), palette)
		draw.Draw(dst, dst.Rect, src, image.ZP, draw.Src)
		g.Image = append(g.Image, dst)
		g.Delay = append(g.Delay, 5)
	}
	file, err := os.Create(path)
	if err != nil {
		return err
	}
	defer file.Close()
	return gif.EncodeAll(file, &g)
}

func screenshot(im image.Image) {
	for i := 0; i < 1000; i++ {
		path := fmt.Sprintf("%03d.png", i)
		if _, err := os.Stat(path); os.IsNotExist(err) {
			savePNG(path, im)
			return
		}
	}
}

func animation(frames []image.Image) {
	for i := 0; i < 1000; i++ {
		path := fmt.Sprintf("%03d.gif", i)
		if _, err := os.Stat(path); os.IsNotExist(err) {
			saveGIF(path, frames)
			return
		}
	}
}

func writeSRAM(filename string, sram []byte) error {
	dir, _ := path.Split(filename)
	if err := os.MkdirAll(dir, 0755); err != nil {
		return err
	}
	file, err := os.Create(filename)
	if err != nil {
		return err
	}
	defer file.Close()
	return binary.Write(file, binary.LittleEndian, sram)
}

func readSRAM(filename string) ([]byte, error) {
	file, err := os.Open(filename)
	if err != nil {
		return nil, err
	}
	defer file.Close()
	sram := make([]byte, 0x2000)
	if err := binary.Read(file, binary.LittleEndian, sram); err != nil {
		return nil, err
	}
	return sram, nil
}
<commit_msg>joystick fix<commit_after>package ui

import (
	"crypto/md5"
	"encoding/binary"
	"fmt"
	"image"
	"image/color"
	"image/draw"
	"image/gif"
	"image/png"
	"io/ioutil"
	"log"
	"os"
	"os/user"
	"path"

	"github.com/fogleman/nes/nes"
	"github.com/go-gl/gl/v2.1/gl"
	"github.com/go-gl/glfw/v3.1/glfw"
)

var homeDir string

func init() {
	u, err := user.Current()
	if err != nil {
		log.Fatalln(err)
	}
	homeDir = u.HomeDir
}

func thumbnailURL(hash string) string {
	return "http://www.michaelfogleman.com/static/nes/" + hash + ".png"
}

func thumbnailPath(hash string) string {
	return homeDir + "/.nes/thumbnail/" + hash + ".png"
}

func sramPath(hash string) string {
	return homeDir + "/.nes/sram/" + hash + ".dat"
}

func savePath(hash string) string {
	return homeDir + "/.nes/save/" + hash + ".dat"
}

func readKey(window *glfw.Window, key glfw.Key) bool {
	return window.GetKey(key) == glfw.Press
}

func readKeys(window *glfw.Window, turbo bool) [8]bool {
	var result [8]bool
	result[nes.ButtonA] = readKey(window, glfw.KeyZ) || (turbo && readKey(window, glfw.KeyA))
	result[nes.ButtonB] = readKey(window, glfw.KeyX) || (turbo && readKey(window, glfw.KeyS))
	result[nes.ButtonSelect] = readKey(window, glfw.KeyRightShift)
	result[nes.ButtonStart] = readKey(window, glfw.KeyEnter)
	result[nes.ButtonUp] = readKey(window, glfw.KeyUp)
	result[nes.ButtonDown] = readKey(window, glfw.KeyDown)
	result[nes.ButtonLeft] = readKey(window, glfw.KeyLeft)
	result[nes.ButtonRight] = readKey(window, glfw.KeyRight)
	return result
}

func readJoystick(joy glfw.Joystick, turbo bool) [8]bool {
	var result [8]bool
	if !glfw.JoystickPresent(joy) {
		return result
	}
	joyname := glfw.GetJoystickName(joy)
	axes := glfw.GetJoystickAxes(joy)
	buttons := glfw.GetJoystickButtons(joy)
	if joyname == "PLAYSTATION(R)3 Controller" {
		result[nes.ButtonA] = buttons[14] == 1 || (turbo && buttons[2] == 1)
		result[nes.ButtonB] = buttons[13] == 1 || (turbo && buttons[3] == 1)
		result[nes.ButtonSelect] = buttons[0] == 1
		result[nes.ButtonStart] = buttons[3] == 1
		result[nes.ButtonUp] = buttons[4] == 1 || axes[1] < -0.5
		result[nes.ButtonDown] = buttons[6] == 1 || axes[1] > 0.5
		result[nes.ButtonLeft] = buttons[7] == 1 || axes[0] < -0.5
		result[nes.ButtonRight] = buttons[5] == 1 || axes[0] > 0.5
		return result
	}
	if len(buttons) < 8 {
		return result
	}
	result[nes.ButtonA] = buttons[0] == 1 || (turbo && buttons[2] == 1)
	result[nes.ButtonB] = buttons[1] == 1 || (turbo && buttons[3] == 1)
	result[nes.ButtonSelect] = buttons[6] == 1
	result[nes.ButtonStart] = buttons[7] == 1
	result[nes.ButtonUp] = axes[1] < -0.5
	result[nes.ButtonDown] = axes[1] > 0.5
	result[nes.ButtonLeft] = axes[0] < -0.5
	result[nes.ButtonRight] = axes[0] > 0.5
	return result
}

func joystickReset(joy glfw.Joystick) bool {
	if !glfw.JoystickPresent(joy) {
		return false
	}
	buttons := glfw.GetJoystickButtons(joy)
	if len(buttons) < 6 {
		return false
	}
	return buttons[4] == 1 && buttons[5] == 1
}

func combineButtons(a, b [8]bool) [8]bool {
	var result [8]bool
	for i := 0; i < 8; i++ {
		result[i] = a[i] || b[i]
	}
	return result
}

func hashFile(path string) (string, error) {
	data, err := ioutil.ReadFile(path)
	if err != nil {
		return "", err
	}
	return fmt.Sprintf("%x", md5.Sum(data)), nil
}

func createTexture() uint32 {
	var texture uint32
	gl.GenTextures(1, &texture)
	gl.BindTexture(gl.TEXTURE_2D, texture)
	gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
	gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
	gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
	gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
	gl.BindTexture(gl.TEXTURE_2D, 0)
	return texture
}

func setTexture(im *image.RGBA) {
	size := im.Rect.Size()
	gl.TexImage2D(
		gl.TEXTURE_2D, 0, gl.RGBA, int32(size.X), int32(size.Y),
		0, gl.RGBA, gl.UNSIGNED_BYTE, gl.Ptr(im.Pix))
}

func copyImage(src image.Image) *image.RGBA {
	dst := image.NewRGBA(src.Bounds())
	draw.Draw(dst, dst.Rect, src, image.ZP, draw.Src)
	return dst
}

func loadPNG(path string) (image.Image, error) {
	file, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer file.Close()
	return png.Decode(file)
}

func savePNG(path string, im image.Image) error {
	file, err := os.Create(path)
	if err != nil {
		return err
	}
	defer file.Close()
	return png.Encode(file, im)
}

func saveGIF(path string, frames []image.Image) error {
	var palette []color.Color
	for _, c := range nes.Palette {
		palette = append(palette, c)
	}
	g := gif.GIF{}
	for i, src := range frames {
		if i%3 != 0 {
			continue
		}
		dst := image.NewPaletted(src.Bounds(), palette)
		draw.Draw(dst, dst.Rect, src, image.ZP, draw.Src)
		g.Image = append(g.Image, dst)
		g.Delay = append(g.Delay, 5)
	}
	file, err := os.Create(path)
	if err != nil {
		return err
	}
	defer file.Close()
	return gif.EncodeAll(file, &g)
}

func screenshot(im image.Image) {
	for i := 0; i < 1000; i++ {
		path := fmt.Sprintf("%03d.png", i)
		if _, err := os.Stat(path); os.IsNotExist(err) {
			savePNG(path, im)
			return
		}
	}
}

func animation(frames []image.Image) {
	for i := 0; i < 1000; i++ {
		path := fmt.Sprintf("%03d.gif", i)
		if _, err := os.Stat(path); os.IsNotExist(err) {
			saveGIF(path, frames)
			return
		}
	}
}

func writeSRAM(filename string, sram []byte) error {
	dir, _ := path.Split(filename)
	if err := os.MkdirAll(dir, 0755); err != nil {
		return err
	}
	file, err := os.Create(filename)
	if err != nil {
		return err
	}
	defer file.Close()
	return binary.Write(file, binary.LittleEndian, sram)
}

func readSRAM(filename string) ([]byte, error) {
	file, err := os.Open(filename)
	if err != nil {
		return nil, err
	}
	defer file.Close()
	sram := make([]byte, 0x2000)
	if err := binary.Read(file, binary.LittleEndian, sram); err != nil {
		return nil, err
	}
	return sram, nil
}
<|endoftext|>