<commit_before>// +build js,wasm

package websocket

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"net"
	"strings"
	"sync"
	"syscall/js"
	"time"
)

const (
	webSocketStateConnecting = 0
	webSocketStateOpen       = 1
	webSocketStateClosing    = 2
	webSocketStateClosed     = 3
)

var errConnectionClosed = errors.New("connection is closed")

// Conn implements net.Conn interface for WebSockets in js/wasm.
type Conn struct {
	js.Value
	messageHandler *js.Func
	closeHandler   *js.Func
	errorHandler   *js.Func
	mut            sync.Mutex
	currDataMut    sync.RWMutex
	currData       bytes.Buffer
	closeOnce      sync.Once
	closeSignal    chan struct{}
	dataSignal     chan struct{}
	localAddr      net.Addr
	remoteAddr     net.Addr
	firstErr       error
}

// NewConn creates a Conn given a regular js/wasm WebSocket Conn.
func NewConn(raw js.Value) *Conn {
	conn := &Conn{
		Value:       raw,
		closeSignal: make(chan struct{}),
		dataSignal:  make(chan struct{}, 1),
		localAddr:   NewAddr("0.0.0.0:0"),
		remoteAddr:  getRemoteAddr(raw),
	}
	// Force the JavaScript WebSockets API to use the ArrayBuffer type for
	// incoming messages instead of the Blob type. This is better for us because
	// ArrayBuffer can be converted to []byte synchronously but Blob cannot.
	conn.Set("binaryType", "arraybuffer")
	conn.setUpHandlers()
	return conn
}

func (c *Conn) Read(b []byte) (int, error) {
	if err := c.checkOpen(); err != nil {
		return c.readAfterErr(b)
	}

	for {
		c.currDataMut.RLock()
		n, err := c.currData.Read(b)
		c.currDataMut.RUnlock()
		if err != nil && err != io.EOF {
			// Return any unexpected errors immediately.
			return n, err
		} else if n == 0 || err == io.EOF {
			// There is no data ready to be read. Wait for more data or for the
			// connection to be closed.
			select {
			case <-c.dataSignal:
				continue
			case <-c.closeSignal:
				return c.readAfterErr(b)
			}
		} else {
			return n, err
		}
	}
}

// readAfterError reads from c.currData. If there is no more data left it
// returns c.firstErr if non-nil and otherwise returns io.EOF.
func (c *Conn) readAfterErr(b []byte) (int, error) {
	c.currDataMut.RLock()
	n, err := c.currData.Read(b)
	c.currDataMut.RUnlock()
	if n == 0 {
		if c.firstErr != nil {
			return 0, c.firstErr
		} else {
			return 0, io.EOF
		}
	}
	return n, err
}

// checkOpen returns an error if the connection is not open. Otherwise, it
// returns nil.
func (c *Conn) checkOpen() error {
	state := c.Get("readyState").Int()
	switch state {
	case webSocketStateClosed, webSocketStateClosing:
		return errConnectionClosed
	}
	return nil
}

func (c *Conn) Write(b []byte) (n int, err error) {
	if err := c.checkOpen(); err != nil {
		return 0, err
	}
	uint8Array := js.Global().Get("Uint8Array").New(len(b))
	for i := 0; i < len(b); i++ {
		uint8Array.SetIndex(i, b[i])
	}
	c.Call("send", uint8Array.Get("buffer"))
	return len(b), nil
}

// Close closes the connection. Only the first call to Close will receive the
// close error, subsequent and concurrent calls will return nil.
// This method is thread-safe.
func (c *Conn) Close() error {
	c.signalClose()
	c.Call("close")
	c.releaseHandlers()
	return nil
}

func (c *Conn) signalClose() {
	c.closeOnce.Do(func() {
		close(c.closeSignal)
	})
}

func (c *Conn) releaseHandlers() {
	c.mut.Lock()
	defer c.mut.Unlock()
	if c.messageHandler != nil {
		c.Call("removeEventListener", "message", *c.messageHandler)
		c.messageHandler.Release()
	}
	if c.closeHandler != nil {
		c.Call("removeEventListener", "close", *c.closeHandler)
		c.closeHandler.Release()
	}
	if c.errorHandler != nil {
		c.Call("removeEventListener", "error", *c.errorHandler)
		c.errorHandler.Release()
	}
}

func (c *Conn) LocalAddr() net.Addr {
	return c.localAddr
}

func getRemoteAddr(val js.Value) net.Addr {
	rawURL := val.Get("url").String()
	withoutPrefix := strings.TrimPrefix(rawURL, "ws://")
	withoutSuffix := strings.TrimSuffix(withoutPrefix, "/")
	return NewAddr(withoutSuffix)
}

func (c *Conn) RemoteAddr() net.Addr {
	return c.remoteAddr
}

// TODO: Return os.ErrNoDeadline. For now we return nil because multiplexers
// don't handle the error correctly.
func (c *Conn) SetDeadline(t time.Time) error {
	return nil
}

func (c *Conn) SetReadDeadline(t time.Time) error {
	return nil
}

func (c *Conn) SetWriteDeadline(t time.Time) error {
	return nil
}

func (c *Conn) setUpHandlers() {
	c.mut.Lock()
	defer c.mut.Unlock()
	if c.messageHandler != nil {
		// Message handlers already created. Nothing to do.
		return
	}
	messageHandler := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		arrayBuffer := args[0].Get("data")
		data := arrayBufferToBytes(arrayBuffer)
		c.currDataMut.Lock()
		if _, err := c.currData.Write(data); err != nil {
			c.currDataMut.Unlock()
			return err
		}
		c.currDataMut.Unlock()

		// Non-blocking signal
		select {
		case c.dataSignal <- struct{}{}:
		default:
		}

		return nil
	})
	c.messageHandler = &messageHandler
	c.Call("addEventListener", "message", messageHandler)

	closeHandler := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		go func() {
			c.signalClose()
			c.mut.Lock()
			// Store the error in c.firstErr. It will be returned by Read later on.
			c.firstErr = errorEventToError(args[0])
			c.mut.Unlock()
			c.releaseHandlers()
		}()
		return nil
	})
	c.closeHandler = &closeHandler
	c.Call("addEventListener", "close", closeHandler)

	errorHandler := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		// Unfortunately, the "error" event doesn't appear to give us any useful
		// information. All we can do is close the connection.
		c.Close()
		return nil
	})
	c.errorHandler = &errorHandler
	c.Call("addEventListener", "error", errorHandler)
}

func (c *Conn) waitForOpen() error {
	openSignal := make(chan struct{})
	handler := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		close(openSignal)
		return nil
	})
	defer c.Call("removeEventListener", "open", handler)
	defer handler.Release()
	c.Call("addEventListener", "open", handler)
	select {
	case <-openSignal:
		return nil
	case <-c.closeSignal:
		// c.closeSignal means there was an error when trying to open the
		// connection.
		return c.firstErr
	}
}

// arrayBufferToBytes converts a JavaScript ArrayBuffer to a slice of bytes.
func arrayBufferToBytes(buffer js.Value) []byte {
	view := js.Global().Get("Uint8Array").New(buffer)
	dataLen := view.Get("length").Int()
	data := make([]byte, dataLen)
	for i := 0; i < dataLen; i++ {
		data[i] = byte(view.Index(i).Int())
	}
	return data
}

func errorEventToError(val js.Value) error {
	var typ string
	if gotType := val.Get("type"); gotType != js.Undefined() {
		typ = gotType.String()
	} else {
		typ = val.Type().String()
	}
	var reason string
	if gotReason := val.Get("reason"); gotReason != js.Undefined() && gotReason.String() != "" {
		reason = gotReason.String()
	} else {
		code := val.Get("code")
		if code != js.Undefined() {
			switch code := code.Int(); code {
			case 1006:
				reason = "code 1006: connection unexpectedly closed"
			default:
				reason = fmt.Sprintf("unexpected code: %d", code)
			}
		}
	}
	return fmt.Errorf("JavaScript error: (%s) %s", typ, reason)
}
<commit_msg>Simplify Conn.Read logic<commit_after>// +build js,wasm

package websocket

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"net"
	"strings"
	"sync"
	"syscall/js"
	"time"
)

const (
	webSocketStateConnecting = 0
	webSocketStateOpen       = 1
	webSocketStateClosing    = 2
	webSocketStateClosed     = 3
)

var errConnectionClosed = errors.New("connection is closed")

// Conn implements net.Conn interface for WebSockets in js/wasm.
type Conn struct {
	js.Value
	messageHandler *js.Func
	closeHandler   *js.Func
	errorHandler   *js.Func
	mut            sync.Mutex
	currDataMut    sync.RWMutex
	currData       bytes.Buffer
	closeOnce      sync.Once
	closeSignal    chan struct{}
	dataSignal     chan struct{}
	localAddr      net.Addr
	remoteAddr     net.Addr
	firstErr       error
}

// NewConn creates a Conn given a regular js/wasm WebSocket Conn.
func NewConn(raw js.Value) *Conn {
	conn := &Conn{
		Value:       raw,
		closeSignal: make(chan struct{}),
		dataSignal:  make(chan struct{}, 1),
		localAddr:   NewAddr("0.0.0.0:0"),
		remoteAddr:  getRemoteAddr(raw),
	}
	// Force the JavaScript WebSockets API to use the ArrayBuffer type for
	// incoming messages instead of the Blob type. This is better for us because
	// ArrayBuffer can be converted to []byte synchronously but Blob cannot.
	conn.Set("binaryType", "arraybuffer")
	conn.setUpHandlers()
	return conn
}

func (c *Conn) Read(b []byte) (int, error) {
	if err := c.checkOpen(); err != nil {
		return c.readAfterErr(b)
	}

	for {
		c.currDataMut.RLock()
		n, _ := c.currData.Read(b)
		c.currDataMut.RUnlock()

		if n != 0 {
			// Data was ready. Return the number of bytes read.
			return n, nil
		}

		// There is no data ready to be read. Wait for more data or for the
		// connection to be closed.
		select {
		case <-c.dataSignal:
			continue
		case <-c.closeSignal:
			return c.readAfterErr(b)
		}
	}
}

// readAfterError reads from c.currData. If there is no more data left it
// returns c.firstErr if non-nil and otherwise returns io.EOF.
func (c *Conn) readAfterErr(b []byte) (int, error) {
	c.currDataMut.RLock()
	n, err := c.currData.Read(b)
	c.currDataMut.RUnlock()
	if n == 0 {
		if c.firstErr != nil {
			return 0, c.firstErr
		} else {
			return 0, io.EOF
		}
	}
	return n, err
}

// checkOpen returns an error if the connection is not open. Otherwise, it
// returns nil.
func (c *Conn) checkOpen() error {
	state := c.Get("readyState").Int()
	switch state {
	case webSocketStateClosed, webSocketStateClosing:
		return errConnectionClosed
	}
	return nil
}

func (c *Conn) Write(b []byte) (n int, err error) {
	if err := c.checkOpen(); err != nil {
		return 0, err
	}
	uint8Array := js.Global().Get("Uint8Array").New(len(b))
	for i := 0; i < len(b); i++ {
		uint8Array.SetIndex(i, b[i])
	}
	c.Call("send", uint8Array.Get("buffer"))
	return len(b), nil
}

// Close closes the connection. Only the first call to Close will receive the
// close error, subsequent and concurrent calls will return nil.
// This method is thread-safe.
func (c *Conn) Close() error {
	c.signalClose()
	c.Call("close")
	c.releaseHandlers()
	return nil
}

func (c *Conn) signalClose() {
	c.closeOnce.Do(func() {
		close(c.closeSignal)
	})
}

func (c *Conn) releaseHandlers() {
	c.mut.Lock()
	defer c.mut.Unlock()
	if c.messageHandler != nil {
		c.Call("removeEventListener", "message", *c.messageHandler)
		c.messageHandler.Release()
	}
	if c.closeHandler != nil {
		c.Call("removeEventListener", "close", *c.closeHandler)
		c.closeHandler.Release()
	}
	if c.errorHandler != nil {
		c.Call("removeEventListener", "error", *c.errorHandler)
		c.errorHandler.Release()
	}
}

func (c *Conn) LocalAddr() net.Addr {
	return c.localAddr
}

func getRemoteAddr(val js.Value) net.Addr {
	rawURL := val.Get("url").String()
	withoutPrefix := strings.TrimPrefix(rawURL, "ws://")
	withoutSuffix := strings.TrimSuffix(withoutPrefix, "/")
	return NewAddr(withoutSuffix)
}

func (c *Conn) RemoteAddr() net.Addr {
	return c.remoteAddr
}

// TODO: Return os.ErrNoDeadline. For now we return nil because multiplexers
// don't handle the error correctly.
func (c *Conn) SetDeadline(t time.Time) error {
	return nil
}

func (c *Conn) SetReadDeadline(t time.Time) error {
	return nil
}

func (c *Conn) SetWriteDeadline(t time.Time) error {
	return nil
}

func (c *Conn) setUpHandlers() {
	c.mut.Lock()
	defer c.mut.Unlock()
	if c.messageHandler != nil {
		// Message handlers already created. Nothing to do.
		return
	}
	messageHandler := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		arrayBuffer := args[0].Get("data")
		data := arrayBufferToBytes(arrayBuffer)
		c.currDataMut.Lock()
		if _, err := c.currData.Write(data); err != nil {
			c.currDataMut.Unlock()
			return err
		}
		c.currDataMut.Unlock()

		// Non-blocking signal
		select {
		case c.dataSignal <- struct{}{}:
		default:
		}

		return nil
	})
	c.messageHandler = &messageHandler
	c.Call("addEventListener", "message", messageHandler)

	closeHandler := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		go func() {
			c.signalClose()
			c.mut.Lock()
			// Store the error in c.firstErr. It will be returned by Read later on.
			c.firstErr = errorEventToError(args[0])
			c.mut.Unlock()
			c.releaseHandlers()
		}()
		return nil
	})
	c.closeHandler = &closeHandler
	c.Call("addEventListener", "close", closeHandler)

	errorHandler := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		// Unfortunately, the "error" event doesn't appear to give us any useful
		// information. All we can do is close the connection.
		c.Close()
		return nil
	})
	c.errorHandler = &errorHandler
	c.Call("addEventListener", "error", errorHandler)
}

func (c *Conn) waitForOpen() error {
	openSignal := make(chan struct{})
	handler := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		close(openSignal)
		return nil
	})
	defer c.Call("removeEventListener", "open", handler)
	defer handler.Release()
	c.Call("addEventListener", "open", handler)
	select {
	case <-openSignal:
		return nil
	case <-c.closeSignal:
		// c.closeSignal means there was an error when trying to open the
		// connection.
		return c.firstErr
	}
}

// arrayBufferToBytes converts a JavaScript ArrayBuffer to a slice of bytes.
func arrayBufferToBytes(buffer js.Value) []byte {
	view := js.Global().Get("Uint8Array").New(buffer)
	dataLen := view.Get("length").Int()
	data := make([]byte, dataLen)
	for i := 0; i < dataLen; i++ {
		data[i] = byte(view.Index(i).Int())
	}
	return data
}

func errorEventToError(val js.Value) error {
	var typ string
	if gotType := val.Get("type"); gotType != js.Undefined() {
		typ = gotType.String()
	} else {
		typ = val.Type().String()
	}
	var reason string
	if gotReason := val.Get("reason"); gotReason != js.Undefined() && gotReason.String() != "" {
		reason = gotReason.String()
	} else {
		code := val.Get("code")
		if code != js.Undefined() {
			switch code := code.Int(); code {
			case 1006:
				reason = "code 1006: connection unexpectedly closed"
			default:
				reason = fmt.Sprintf("unexpected code: %d", code)
			}
		}
	}
	return fmt.Errorf("JavaScript error: (%s) %s", typ, reason)
}
<|endoftext|>


<commit_before>package graph

import "testing"
import . "github.com/smartystreets/goconvey/convey"

func TestStruct(t *testing.T) {
	// Vertices
	Convey("Vertex Testing", t, func() {
		al := NewAdjList()
		Convey("Add Two Node", func() {

			al.SetVertex(1, "hello")
			al.SetVertex(2, "world")

			Convey("Get Node Val", func() {
				So(al.GetVertex(1), ShouldEqual, "hello")
				So(al.getNode(2).id, ShouldEqual, 2)
				So(al.GetVertex(3), ShouldBeNil)
			})

			So(len(*al), ShouldEqual, 2)
			So((*al)[0].node.val, ShouldEqual, "hello")
			So((*al)[1].next, ShouldBeNil)
		})

		Convey("Update Exist Nodes", func() {
			al.SetVertex(1, "howdy")
			So(al.GetVertex(1), ShouldEqual, "howdy")
		})

		Convey("Del Nodes Without Removing The Related Edges", func() {
			al.DelVertex(1)
			So(len(*al), ShouldEqual, 1)
			So((*al)[0].id, ShouldEqual, 2)
			al.DelVertex(2)
			So(len(*al), ShouldEqual, 0)
		})
	})

	// Edges
	Convey("Edge Testing", t, func() {
		al := NewAdjList()
		al.SetVertex(0, "hello")
		al.SetVertex(1, "world")
		al.SetVertex(2, "!")
		Convey("Add Edge", func() {
			al.SetEdge(0, 1, "0->1")
			al.SetEdge(0, 2, "0->2")
			al.SetEdge(1, 2, "1->2")
			So((*al)[0].next, ShouldNotBeNil)
			So((*al)[0].next.id, ShouldEqual, 1)
			So((*al)[0].next.edge, ShouldEqual, "0->1")
		})

		Convey("Get Edge Val", func() {
			e := al.GetEdge(0, 1)
			So(e, ShouldEqual, "0->1")
			So(al.GetEdge(1, 0), ShouldBeNil)
			So(al.GetEdge(1, 2), ShouldEqual, "1->2")
		})

		Convey("Update Edge", func() {
			al.SetEdge(0, 1, "0-1")
			al.SetEdge(0, 2, "0-2")
			So(al.GetEdge(0, 1), ShouldEqual, "0-1")
			So(al.GetEdge(0, 2), ShouldEqual, "0-2")
			So(al.GetEdge(1, 2), ShouldEqual, "1->2")
		})

		Convey("Del Certain Edge Without Side Effects", func() {
			al.DelEdge(0, 2)
			al.DelEdge(0, 1)
			So(al.GetEdge(0, 1), ShouldBeNil)
			So(al.GetEdge(1, 2), ShouldEqual, "1->2")
			So(al.GetEdge(0, 2), ShouldBeNil)
		})
	})
}

func TestNeighbours(t *testing.T) {
	al := NewAdjList()
	al.SetVertex(0, "hello")
	al.SetVertex(1, "world")
	al.SetVertex(2, "!")
	al.SetEdge(0, 1, "0->1")
	al.SetEdge(0, 2, "0->2")
	al.SetEdge(2, 0, "2->0")

	Convey("Is Neighbour", t, func() {
		So(al.IsAdjacent(0, 1), ShouldBeTrue)
		So(al.IsAdjacent(0, 2), ShouldBeTrue)
		So(al.IsAdjacent(2, 0), ShouldBeTrue)
		So(al.IsAdjacent(2, 1), ShouldBeFalse)
	})

	Convey("Get Neighbours", t, func() {
		nb0 := al.GetNeighbours(0)
		So(len(nb0), ShouldEqual, 2)
		So(nb0[1], ShouldEqual, 2)
	})

	Convey("Get Inverse Neighbours", t, func() {
		nb0 := al.GetInverseNbs(0)
		So(len(nb0), ShouldEqual, 1)
		So(nb0[0], ShouldEqual, 2)
	})

}

func TestIteration(t *testing.T) {

	Convey("Iter Vertices", t, func() {

	})

	Convey("Iter Edges", t, func() {

	})

}

func TestInterface(t *testing.T) {
	Convey("Adjacency List Implemention", t, func() {
		foo := func(g Graph) {}
		al := NewAdjList()
		Convey("Implementated Graph Interface", func() {
			foo(al)
		})

	})
}
<commit_msg>Add test case for iteration operations<commit_after>package graph

import "testing"
import . "github.com/smartystreets/goconvey/convey"

func TestStruct(t *testing.T) {
	// Vertices
	Convey("Vertex Testing", t, func() {
		al := NewAdjList()
		Convey("Add Two Node", func() {

			al.SetVertex(1, "hello")
			al.SetVertex(2, "world")

			Convey("Get Node Val", func() {
				So(al.GetVertex(1), ShouldEqual, "hello")
				So(al.getNode(2).id, ShouldEqual, 2)
				So(al.GetVertex(3), ShouldBeNil)
			})

			So(len(*al), ShouldEqual, 2)
			So((*al)[0].node.val, ShouldEqual, "hello")
			So((*al)[1].next, ShouldBeNil)
		})

		Convey("Update Exist Nodes", func() {
			al.SetVertex(1, "howdy")
			So(al.GetVertex(1), ShouldEqual, "howdy")
		})

		Convey("Del Nodes Without Removing The Related Edges", func() {
			al.DelVertex(1)
			So(len(*al), ShouldEqual, 1)
			So((*al)[0].id, ShouldEqual, 2)
			al.DelVertex(2)
			So(len(*al), ShouldEqual, 0)
		})
	})

	// Edges
	Convey("Edge Testing", t, func() {
		al := NewAdjList()
		al.SetVertex(0, "hello")
		al.SetVertex(1, "world")
		al.SetVertex(2, "!")
		Convey("Add Edge", func() {
			al.SetEdge(0, 1, "0->1")
			al.SetEdge(0, 2, "0->2")
			al.SetEdge(1, 2, "1->2")
			So((*al)[0].next, ShouldNotBeNil)
			So((*al)[0].next.id, ShouldEqual, 1)
			So((*al)[0].next.edge, ShouldEqual, "0->1")
		})

		Convey("Get Edge Val", func() {
			e := al.GetEdge(0, 1)
			So(e, ShouldEqual, "0->1")
			So(al.GetEdge(1, 0), ShouldBeNil)
			So(al.GetEdge(1, 2), ShouldEqual, "1->2")
		})

		Convey("Update Edge", func() {
			al.SetEdge(0, 1, "0-1")
			al.SetEdge(0, 2, "0-2")
			So(al.GetEdge(0, 1), ShouldEqual, "0-1")
			So(al.GetEdge(0, 2), ShouldEqual, "0-2")
			So(al.GetEdge(1, 2), ShouldEqual, "1->2")
		})

		Convey("Del Certain Edge Without Side Effects", func() {
			al.DelEdge(0, 2)
			al.DelEdge(0, 1)
			So(al.GetEdge(0, 1), ShouldBeNil)
			So(al.GetEdge(1, 2), ShouldEqual, "1->2")
			So(al.GetEdge(0, 2), ShouldBeNil)
		})
	})
}

func TestNeighbours(t *testing.T) {
	al := NewAdjList()
	al.SetVertex(0, "hello")
	al.SetVertex(1, "world")
	al.SetVertex(2, "!")
	al.SetEdge(0, 1, "0->1")
	al.SetEdge(0, 2, "0->2")
	al.SetEdge(2, 0, "2->0")

	Convey("Is Neighbour", t, func() {
		So(al.IsAdjacent(0, 1), ShouldBeTrue)
		So(al.IsAdjacent(0, 2), ShouldBeTrue)
		So(al.IsAdjacent(2, 0), ShouldBeTrue)
		So(al.IsAdjacent(2, 1), ShouldBeFalse)
	})

	Convey("Get Neighbours", t, func() {
		nb0 := al.GetNeighbours(0)
		So(len(nb0), ShouldEqual, 2)
		So(nb0[1], ShouldEqual, 2)
	})

	Convey("Get Inverse Neighbours", t, func() {
		nb0 := al.GetInverseNbs(0)
		So(len(nb0), ShouldEqual, 1)
		So(nb0[0], ShouldEqual, 2)
	})

}

func TestIteration(t *testing.T) {
	al := NewAdjList()
	al.SetVertex(0, "hello")
	al.SetVertex(1, "world")
	al.SetVertex(2, "!")
	al.SetEdge(0, 1, "0->1")
	al.SetEdge(0, 2, "0->2")
	al.SetEdge(2, 0, "2->0")
	Convey("Iter Vertices", t, func() {
		al.IterVertices(func(g Graph, id int) {
			g.SetVertex(id, "*")
		})
		So(al.GetVertex(0), ShouldEqual, "*")
		So(al.GetVertex(1), ShouldEqual, "*")
		So(al.GetVertex(2), ShouldEqual, "*")
		So(al.GetVertex(3), ShouldBeNil)
	})

	Convey("Iter Edges", t, func() {
		al.IterEdges(func(g Graph, from, to int) {
			g.SetEdge(from, to, "-")
		})
		So(al.GetEdge(0, 1), ShouldEqual, "-")
		So(al.GetEdge(0, 2), ShouldEqual, "-")
		So(al.GetEdge(2, 0), ShouldEqual, "-")
		So(al.GetEdge(2, 1), ShouldBeNil)
	})

}

func TestInterface(t *testing.T) {
	Convey("Adjacency List Implemention", t, func() {
		foo := func(g Graph) {}
		al := NewAdjList()
		Convey("Implementated Graph Interface", func() {
			foo(al)
		})

	})
}
<|endoftext|>


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

package main

import (
	"flag"
	"fmt"
	"go/build"
	"internal/testenv"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"sync"
	"testing"
)

func TestMain(m *testing.M) {
	if !testenv.HasExec() {
		os.Stdout.WriteString("skipping test: platform cannot exec")
		os.Exit(0)
	}

	flag.Parse()
	for _, c := range contexts {
		c.Compiler = build.Default.Compiler
	}
	build.Default.GOROOT = testenv.GOROOT(nil)

	// Warm up the import cache in parallel.
	var wg sync.WaitGroup
	for _, context := range contexts {
		context := context
		wg.Add(1)
		go func() {
			defer wg.Done()
			_ = NewWalker(context, filepath.Join(build.Default.GOROOT, "src"))
		}()
	}
	wg.Wait()

	os.Exit(m.Run())
}

var (
	updateGolden = flag.Bool("updategolden", false, "update golden files")
)

func TestGolden(t *testing.T) {
	td, err := os.Open("testdata/src/pkg")
	if err != nil {
		t.Fatal(err)
	}
	fis, err := td.Readdir(0)
	if err != nil {
		t.Fatal(err)
	}
	for _, fi := range fis {
		if !fi.IsDir() {
			continue
		}

		// TODO(gri) remove extra pkg directory eventually
		goldenFile := filepath.Join("testdata", "src", "pkg", fi.Name(), "golden.txt")
		w := NewWalker(nil, "testdata/src/pkg")
		pkg, _ := w.Import(fi.Name())
		w.export(pkg)

		if *updateGolden {
			os.Remove(goldenFile)
			f, err := os.Create(goldenFile)
			if err != nil {
				t.Fatal(err)
			}
			for _, feat := range w.Features() {
				fmt.Fprintf(f, "%s\n", feat)
			}
			f.Close()
		}

		bs, err := os.ReadFile(goldenFile)
		if err != nil {
			t.Fatalf("opening golden.txt for package %q: %v", fi.Name(), err)
		}
		wanted := strings.Split(string(bs), "\n")
		sort.Strings(wanted)
		for _, feature := range wanted {
			if feature == "" {
				continue
			}
			_, ok := w.features[feature]
			if !ok {
				t.Errorf("package %s: missing feature %q", fi.Name(), feature)
			}
			delete(w.features, feature)
		}

		for _, feature := range w.Features() {
			t.Errorf("package %s: extra feature not in golden file: %q", fi.Name(), feature)
		}
	}
}

func TestCompareAPI(t *testing.T) {
	tests := []struct {
		name                                    string
		features, required, optional, exception []string
		ok                                      bool   // want
		out                                     string // want
	}{
		{
			name:     "feature added",
			features: []string{"A", "B", "C", "D", "E", "F"},
			required: []string{"B", "D"},
			ok:       true,
			out:      "+A\n+C\n+E\n+F\n",
		},
		{
			name:     "feature removed",
			features: []string{"C", "A"},
			required: []string{"A", "B", "C"},
			ok:       false,
			out:      "-B\n",
		},
		{
			name:     "feature added then removed",
			features: []string{"A", "C"},
			optional: []string{"B"},
			required: []string{"A", "C"},
			ok:       true,
			out:      "±B\n",
		},
		{
			name:      "exception removal",
			required:  []string{"A", "B", "C"},
			features:  []string{"A", "C"},
			exception: []string{"B"},
			ok:        true,
			out:       "",
		},
		{
			// https://golang.org/issue/4303
			name: "contexts reconverging",
			required: []string{
				"A",
				"pkg syscall (darwin-amd64), type RawSockaddrInet6 struct",
			},
			features: []string{
				"A",
				"pkg syscall, type RawSockaddrInet6 struct",
			},
			ok:  true,
			out: "+pkg syscall, type RawSockaddrInet6 struct\n",
		},
	}
	for _, tt := range tests {
		buf := new(strings.Builder)
		gotok := compareAPI(buf, tt.features, tt.required, tt.optional, tt.exception, true)
		if gotok != tt.ok {
			t.Errorf("%s: ok = %v; want %v", tt.name, gotok, tt.ok)
		}
		if got := buf.String(); got != tt.out {
			t.Errorf("%s: output differs\nGOT:\n%s\nWANT:\n%s", tt.name, got, tt.out)
		}
	}
}

func TestSkipInternal(t *testing.T) {
	tests := []struct {
		pkg  string
		want bool
	}{
		{"net/http", true},
		{"net/http/internal-foo", true},
		{"net/http/internal", false},
		{"net/http/internal/bar", false},
		{"internal/foo", false},
		{"internal", false},
	}
	for _, tt := range tests {
		got := !internalPkg.MatchString(tt.pkg)
		if got != tt.want {
			t.Errorf("%s is internal = %v; want %v", tt.pkg, got, tt.want)
		}
	}
}

func BenchmarkAll(b *testing.B) {
	for i := 0; i < b.N; i++ {
		for _, context := range contexts {
			w := NewWalker(context, filepath.Join(build.Default.GOROOT, "src"))
			for _, name := range w.stdPackages {
				pkg, _ := w.Import(name)
				w.export(pkg)
			}
			w.Features()
		}
	}
}

func TestIssue21181(t *testing.T) {
	for _, context := range contexts {
		w := NewWalker(context, "testdata/src/issue21181")
		pkg, err := w.Import("p")
		if err != nil {
			t.Fatalf("%s: (%s-%s) %s %v", err, context.GOOS, context.GOARCH,
				pkg.Name(), w.imported)
		}
		w.export(pkg)
	}
}

func TestIssue29837(t *testing.T) {
	for _, context := range contexts {
		w := NewWalker(context, "testdata/src/issue29837")
		_, err := w.Import("p")
		if _, nogo := err.(*build.NoGoError); !nogo {
			t.Errorf("expected *build.NoGoError, got %T", err)
		}
	}
}

func TestIssue41358(t *testing.T) {
	context := new(build.Context)
	*context = build.Default
	context.Dir = filepath.Join(context.GOROOT, "src")

	w := NewWalker(context, context.Dir)
	for _, pkg := range w.stdPackages {
		if strings.HasPrefix(pkg, "vendor/") || strings.HasPrefix(pkg, "golang.org/x/") {
			t.Fatalf("stdPackages contains unexpected package %s", pkg)
		}
	}
}
<commit_msg>cmd/api: skip tests when 'os/exec' is supported but 'go build' is not<commit_after>// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"flag"
	"fmt"
	"go/build"
	"internal/testenv"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"sync"
	"testing"
)

func TestMain(m *testing.M) {
	if !testenv.HasExec() {
		os.Stdout.WriteString("skipping test: platform cannot exec")
		os.Exit(0)
	}
	if !testenv.HasGoBuild() {
		os.Stdout.WriteString("skipping test: platform cannot 'go build' to import std packages")
		os.Exit(0)
	}

	flag.Parse()
	for _, c := range contexts {
		c.Compiler = build.Default.Compiler
	}
	build.Default.GOROOT = testenv.GOROOT(nil)

	// Warm up the import cache in parallel.
	var wg sync.WaitGroup
	for _, context := range contexts {
		context := context
		wg.Add(1)
		go func() {
			defer wg.Done()
			_ = NewWalker(context, filepath.Join(build.Default.GOROOT, "src"))
		}()
	}
	wg.Wait()

	os.Exit(m.Run())
}

var (
	updateGolden = flag.Bool("updategolden", false, "update golden files")
)

func TestGolden(t *testing.T) {
	td, err := os.Open("testdata/src/pkg")
	if err != nil {
		t.Fatal(err)
	}
	fis, err := td.Readdir(0)
	if err != nil {
		t.Fatal(err)
	}
	for _, fi := range fis {
		if !fi.IsDir() {
			continue
		}

		// TODO(gri) remove extra pkg directory eventually
		goldenFile := filepath.Join("testdata", "src", "pkg", fi.Name(), "golden.txt")
		w := NewWalker(nil, "testdata/src/pkg")
		pkg, _ := w.Import(fi.Name())
		w.export(pkg)

		if *updateGolden {
			os.Remove(goldenFile)
			f, err := os.Create(goldenFile)
			if err != nil {
				t.Fatal(err)
			}
			for _, feat := range w.Features() {
				fmt.Fprintf(f, "%s\n", feat)
			}
			f.Close()
		}

		bs, err := os.ReadFile(goldenFile)
		if err != nil {
			t.Fatalf("opening golden.txt for package %q: %v", fi.Name(), err)
		}
		wanted := strings.Split(string(bs), "\n")
		sort.Strings(wanted)
		for _, feature := range wanted {
			if feature == "" {
				continue
			}
			_, ok := w.features[feature]
			if !ok {
				t.Errorf("package %s: missing feature %q", fi.Name(), feature)
			}
			delete(w.features, feature)
		}

		for _, feature := range w.Features() {
			t.Errorf("package %s: extra feature not in golden file: %q", fi.Name(), feature)
		}
	}
}

func TestCompareAPI(t *testing.T) {
	tests := []struct {
		name                                    string
		features, required, optional, exception []string
		ok                                      bool   // want
		out                                     string // want
	}{
		{
			name:     "feature added",
			features: []string{"A", "B", "C", "D", "E", "F"},
			required: []string{"B", "D"},
			ok:       true,
			out:      "+A\n+C\n+E\n+F\n",
		},
		{
			name:     "feature removed",
			features: []string{"C", "A"},
			required: []string{"A", "B", "C"},
			ok:       false,
			out:      "-B\n",
		},
		{
			name:     "feature added then removed",
			features: []string{"A", "C"},
			optional: []string{"B"},
			required: []string{"A", "C"},
			ok:       true,
			out:      "±B\n",
		},
		{
			name:      "exception removal",
			required:  []string{"A", "B", "C"},
			features:  []string{"A", "C"},
			exception: []string{"B"},
			ok:        true,
			out:       "",
		},
		{
			// https://golang.org/issue/4303
			name: "contexts reconverging",
			required: []string{
				"A",
				"pkg syscall (darwin-amd64), type RawSockaddrInet6 struct",
			},
			features: []string{
				"A",
				"pkg syscall, type RawSockaddrInet6 struct",
			},
			ok:  true,
			out: "+pkg syscall, type RawSockaddrInet6 struct\n",
		},
	}
	for _, tt := range tests {
		buf := new(strings.Builder)
		gotok := compareAPI(buf, tt.features, tt.required, tt.optional, tt.exception, true)
		if gotok != tt.ok {
			t.Errorf("%s: ok = %v; want %v", tt.name, gotok, tt.ok)
		}
		if got := buf.String(); got != tt.out {
			t.Errorf("%s: output differs\nGOT:\n%s\nWANT:\n%s", tt.name, got, tt.out)
		}
	}
}

func TestSkipInternal(t *testing.T) {
	tests := []struct {
		pkg  string
		want bool
	}{
		{"net/http", true},
		{"net/http/internal-foo", true},
		{"net/http/internal", false},
		{"net/http/internal/bar", false},
		{"internal/foo", false},
		{"internal", false},
	}
	for _, tt := range tests {
		got := !internalPkg.MatchString(tt.pkg)
		if got != tt.want {
			t.Errorf("%s is internal = %v; want %v", tt.pkg, got, tt.want)
		}
	}
}

func BenchmarkAll(b *testing.B) {
	for i := 0; i < b.N; i++ {
		for _, context := range contexts {
			w := NewWalker(context, filepath.Join(build.Default.GOROOT, "src"))
			for _, name := range w.stdPackages {
				pkg, _ := w.Import(name)
				w.export(pkg)
			}
			w.Features()
		}
	}
}

func TestIssue21181(t *testing.T) {
	for _, context := range contexts {
		w := NewWalker(context, "testdata/src/issue21181")
		pkg, err := w.Import("p")
		if err != nil {
			t.Fatalf("%s: (%s-%s) %s %v", err, context.GOOS, context.GOARCH,
				pkg.Name(), w.imported)
		}
		w.export(pkg)
	}
}

func TestIssue29837(t *testing.T) {
	for _, context := range contexts {
		w := NewWalker(context, "testdata/src/issue29837")
		_, err := w.Import("p")
		if _, nogo := err.(*build.NoGoError); !nogo {
			t.Errorf("expected *build.NoGoError, got %T", err)
		}
	}
}

func TestIssue41358(t *testing.T) {
	context := new(build.Context)
	*context = build.Default
	context.Dir = filepath.Join(context.GOROOT, "src")

	w := NewWalker(context, context.Dir)
	for _, pkg := range w.stdPackages {
		if strings.HasPrefix(pkg, "vendor/") || strings.HasPrefix(pkg, "golang.org/x/") {
			t.Fatalf("stdPackages contains unexpected package %s", pkg)
		}
	}
}
<|endoftext|>


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

// Build toolchain using Go 1.4.
//
// The general strategy is to copy the source files we need into
// a new GOPATH workspace, adjust import paths appropriately,
// invoke the Go 1.4 go command to build those sources,
// and then copy the binaries back.

package main

import (
	"os"
	"strings"
)

// bootstrapDirs is a list of directories holding code that must be
// compiled with a Go 1.4 toolchain to produce the bootstrapTargets.
// All directories in this list are relative to and must be below $GOROOT/src/cmd.
// The list is assumed to have two kinds of entries: names without slashes,
// which are commands, and entries beginning with internal/, which are
// packages supporting the commands.
var bootstrapDirs = []string{
	"asm",
	"asm/internal/arch",
	"asm/internal/asm",
	"asm/internal/flags",
	"asm/internal/lex",
	"compile",
	"compile/internal/amd64",
	"compile/internal/arm",
	"compile/internal/arm64",
	"compile/internal/big",
	"compile/internal/gc",
	"compile/internal/ppc64",
	"compile/internal/x86",
	"internal/gcprog",
	"internal/obj",
	"internal/obj/arm",
	"internal/obj/arm64",
	"internal/obj/ppc64",
	"internal/obj/sparc64",
	"internal/obj/x86",
	"link",
	"link/internal/amd64",
	"link/internal/arm",
	"link/internal/arm64",
	"link/internal/ld",
	"link/internal/ppc64",
	"link/internal/x86",
}

func bootstrapBuildTools() {
	goroot_bootstrap := os.Getenv("GOROOT_BOOTSTRAP")
	if goroot_bootstrap == "" {
		goroot_bootstrap = pathf("%s/go1.4", os.Getenv("HOME"))
	}
	xprintf("##### Building Go toolchain using %s.\n", goroot_bootstrap)

	mkzbootstrap(pathf("%s/src/cmd/internal/obj/zbootstrap.go", goroot))

	// Use $GOROOT/pkg/bootstrap as the bootstrap workspace root.
	// We use a subdirectory of $GOROOT/pkg because that's the
	// space within $GOROOT where we store all generated objects.
	// We could use a temporary directory outside $GOROOT instead,
	// but it is easier to debug on failure if the files are in a known location.
	workspace := pathf("%s/pkg/bootstrap", goroot)
	xremoveall(workspace)
	base := pathf("%s/src/bootstrap", workspace)
	xmkdirall(base)

	// Copy source code into $GOROOT/pkg/bootstrap and rewrite import paths.
	for _, dir := range bootstrapDirs {
		src := pathf("%s/src/cmd/%s", goroot, dir)
		dst := pathf("%s/%s", base, dir)
		xmkdirall(dst)
		for _, name := range xreaddirfiles(src) {
			srcFile := pathf("%s/%s", src, name)
			text := readfile(srcFile)
			text = bootstrapFixImports(text, srcFile)
			writefile(text, pathf("%s/%s", dst, name), 0)
		}
	}

	// Set up environment for invoking Go 1.4 go command.
	// GOROOT points at Go 1.4 GOROOT,
	// GOPATH points at our bootstrap workspace,
	// GOBIN is empty, so that binaries are installed to GOPATH/bin,
	// and GOOS, GOHOSTOS, GOARCH, and GOHOSTOS are empty,
	// so that Go 1.4 builds whatever kind of binary it knows how to build.
	// Restore GOROOT, GOPATH, and GOBIN when done.
	// Don't bother with GOOS, GOHOSTOS, GOARCH, and GOHOSTARCH,
	// because setup will take care of those when bootstrapBuildTools returns.

	defer os.Setenv("GOROOT", os.Getenv("GOROOT"))
	os.Setenv("GOROOT", goroot_bootstrap)

	defer os.Setenv("GOPATH", os.Getenv("GOPATH"))
	os.Setenv("GOPATH", workspace)

	defer os.Setenv("GOBIN", os.Getenv("GOBIN"))
	os.Setenv("GOBIN", "")

	os.Setenv("GOOS", "")
	os.Setenv("GOHOSTOS", "")
	os.Setenv("GOARCH", "")
	os.Setenv("GOHOSTARCH", "")

	// Run Go 1.4 to build binaries.
	run(workspace, ShowOutput|CheckExit, pathf("%s/bin/go", goroot_bootstrap), "install", "-v", "bootstrap/...")

	// Copy binaries into tool binary directory.
	for _, name := range bootstrapDirs {
		if !strings.Contains(name, "/") {
			copyfile(pathf("%s/%s%s", tooldir, name, exe), pathf("%s/bin/%s%s", workspace, name, exe), writeExec)
		}
	}

	xprintf("\n")
}

func bootstrapFixImports(text, srcFile string) string {
	lines := strings.SplitAfter(text, "\n")
	inBlock := false
	for i, line := range lines {
		if strings.HasPrefix(line, "import (") {
			inBlock = true
			continue
		}
		if inBlock && strings.HasPrefix(line, ")") {
			inBlock = false
			continue
		}
		if strings.HasPrefix(line, `import "`) || strings.HasPrefix(line, `import . "`) ||
			inBlock && (strings.HasPrefix(line, "\t\"") || strings.HasPrefix(line, "\t. \"")) {
			lines[i] = strings.Replace(line, `"cmd/`, `"bootstrap/`, -1)
		}
	}

	lines[0] = "// Do not edit. Bootstrap copy of " + srcFile + "\n\n//line " + srcFile + ":1\n" + lines[0]

	return strings.Join(lines, "")
}
<commit_msg>cmd/dist: add link/internal/sparc64 to bootstrapDirs<commit_after>// Copyright 2015 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Build toolchain using Go 1.4.
//
// The general strategy is to copy the source files we need into
// a new GOPATH workspace, adjust import paths appropriately,
// invoke the Go 1.4 go command to build those sources,
// and then copy the binaries back.

package main

import (
	"os"
	"strings"
)

// bootstrapDirs is a list of directories holding code that must be
// compiled with a Go 1.4 toolchain to produce the bootstrapTargets.
// All directories in this list are relative to and must be below $GOROOT/src/cmd.
// The list is assumed to have two kinds of entries: names without slashes,
// which are commands, and entries beginning with internal/, which are
// packages supporting the commands.
var bootstrapDirs = []string{
	"asm",
	"asm/internal/arch",
	"asm/internal/asm",
	"asm/internal/flags",
	"asm/internal/lex",
	"compile",
	"compile/internal/amd64",
	"compile/internal/arm",
	"compile/internal/arm64",
	"compile/internal/big",
	"compile/internal/gc",
	"compile/internal/ppc64",
	"compile/internal/x86",
	"internal/gcprog",
	"internal/obj",
	"internal/obj/arm",
	"internal/obj/arm64",
	"internal/obj/ppc64",
	"internal/obj/sparc64",
	"internal/obj/x86",
	"link",
	"link/internal/amd64",
	"link/internal/arm",
	"link/internal/arm64",
	"link/internal/ld",
	"link/internal/ppc64",
	"link/internal/sparc64",
	"link/internal/x86",
}

func bootstrapBuildTools() {
	goroot_bootstrap := os.Getenv("GOROOT_BOOTSTRAP")
	if goroot_bootstrap == "" {
		goroot_bootstrap = pathf("%s/go1.4", os.Getenv("HOME"))
	}
	xprintf("##### Building Go toolchain using %s.\n", goroot_bootstrap)

	mkzbootstrap(pathf("%s/src/cmd/internal/obj/zbootstrap.go", goroot))

	// Use $GOROOT/pkg/bootstrap as the bootstrap workspace root.
	// We use a subdirectory of $GOROOT/pkg because that's the
	// space within $GOROOT where we store all generated objects.
	// We could use a temporary directory outside $GOROOT instead,
	// but it is easier to debug on failure if the files are in a known location.
	workspace := pathf("%s/pkg/bootstrap", goroot)
	xremoveall(workspace)
	base := pathf("%s/src/bootstrap", workspace)
	xmkdirall(base)

	// Copy source code into $GOROOT/pkg/bootstrap and rewrite import paths.
	for _, dir := range bootstrapDirs {
		src := pathf("%s/src/cmd/%s", goroot, dir)
		dst := pathf("%s/%s", base, dir)
		xmkdirall(dst)
		for _, name := range xreaddirfiles(src) {
			srcFile := pathf("%s/%s", src, name)
			text := readfile(srcFile)
			text = bootstrapFixImports(text, srcFile)
			writefile(text, pathf("%s/%s", dst, name), 0)
		}
	}

	// Set up environment for invoking Go 1.4 go command.
	// GOROOT points at Go 1.4 GOROOT,
	// GOPATH points at our bootstrap workspace,
	// GOBIN is empty, so that binaries are installed to GOPATH/bin,
	// and GOOS, GOHOSTOS, GOARCH, and GOHOSTOS are empty,
	// so that Go 1.4 builds whatever kind of binary it knows how to build.
	// Restore GOROOT, GOPATH, and GOBIN when done.
	// Don't bother with GOOS, GOHOSTOS, GOARCH, and GOHOSTARCH,
	// because setup will take care of those when bootstrapBuildTools returns.

	defer os.Setenv("GOROOT", os.Getenv("GOROOT"))
	os.Setenv("GOROOT", goroot_bootstrap)

	defer os.Setenv("GOPATH", os.Getenv("GOPATH"))
	os.Setenv("GOPATH", workspace)

	defer os.Setenv("GOBIN", os.Getenv("GOBIN"))
	os.Setenv("GOBIN", "")

	os.Setenv("GOOS", "")
	os.Setenv("GOHOSTOS", "")
	os.Setenv("GOARCH", "")
	os.Setenv("GOHOSTARCH", "")

	// Run Go 1.4 to build binaries.
	run(workspace, ShowOutput|CheckExit, pathf("%s/bin/go", goroot_bootstrap), "install", "-v", "bootstrap/...")

	// Copy binaries into tool binary directory.
	for _, name := range bootstrapDirs {
		if !strings.Contains(name, "/") {
			copyfile(pathf("%s/%s%s", tooldir, name, exe), pathf("%s/bin/%s%s", workspace, name, exe), writeExec)
		}
	}

	xprintf("\n")
}

func bootstrapFixImports(text, srcFile string) string {
	lines := strings.SplitAfter(text, "\n")
	inBlock := false
	for i, line := range lines {
		if strings.HasPrefix(line, "import (") {
			inBlock = true
			continue
		}
		if inBlock && strings.HasPrefix(line, ")") {
			inBlock = false
			continue
		}
		if strings.HasPrefix(line, `import "`) || strings.HasPrefix(line, `import . "`) ||
			inBlock && (strings.HasPrefix(line, "\t\"") || strings.HasPrefix(line, "\t. \"")) {
			lines[i] = strings.Replace(line, `"cmd/`, `"bootstrap/`, -1)
		}
	}

	lines[0] = "// Do not edit. Bootstrap copy of " + srcFile + "\n\n//line " + srcFile + ":1\n" + lines[0]

	return strings.Join(lines, "")
}
<|endoftext|>


<commit_before>package baggageclaimcmd

import (
	"fmt"
	"net/http"
	"os"
	"time"

	"code.cloudfoundry.org/clock"
	"code.cloudfoundry.org/lager"
	"github.com/concourse/baggageclaim/api"
	"github.com/concourse/baggageclaim/reaper"
	"github.com/concourse/baggageclaim/uidgid"
	"github.com/concourse/baggageclaim/volume"
	"github.com/concourse/flag"
	"github.com/tedsuo/ifrit"
	"github.com/tedsuo/ifrit/grouper"
	"github.com/tedsuo/ifrit/http_server"
	"github.com/tedsuo/ifrit/sigmon"
	"github.com/xoebus/zest"
)

type BaggageclaimCommand struct {
	Logger flag.Lager

	BindIP        flag.IP `long:"bind-ip"   default:"127.0.0.1" description:"IP address on which to listen for API traffic."`
	BindPort      uint16  `long:"bind-port" default:"7788"      description:"Port on which to listen for API traffic."`
	DebugBindPort uint16  `long:"bind-debug-port" default:"8099"    description:"Port on which to listen for baggageclaim pprof server."`

	VolumesDir flag.Dir `long:"volumes" required:"true" description:"Directory in which to place volume data."`

	Driver string `long:"driver" default:"detect" choice:"detect" choice:"naive" choice:"btrfs" choice:"overlay" description:"Driver to use for managing volumes."`

	BtrfsBin string `long:"btrfs-bin" default:"btrfs" description:"Path to btrfs binary"`
	MkfsBin  string `long:"mkfs-bin" default:"mkfs.btrfs" description:"Path to mkfs.btrfs binary"`

	OverlaysDir string `long:"overlays-dir" description:"Path to directory in which to store overlay data"`

	ReapInterval time.Duration `long:"reap-interval" default:"10s" description:"Interval on which to reap expired volumes."`

	Metrics struct {
		YellerAPIKey      string `long:"yeller-api-key"     description:"Yeller API key. If specified, all errors logged will be emitted."`
		YellerEnvironment string `long:"yeller-environment" description:"Environment to tag on all Yeller events emitted."`
	} `group:"Metrics & Diagnostics"`
}

func (cmd *BaggageclaimCommand) debugBindAddr() string {
	return fmt.Sprintf("127.0.0.1:%d", cmd.DebugBindPort)
}

func (cmd *BaggageclaimCommand) Execute(args []string) error {
	runner, err := cmd.Runner(args)
	if err != nil {
		return err
	}

	return <-ifrit.Invoke(sigmon.New(runner)).Wait()
}

func (cmd *BaggageclaimCommand) Runner(args []string) (ifrit.Runner, error) {
	logger, _ := cmd.constructLogger()

	listenAddr := fmt.Sprintf("%s:%d", cmd.BindIP.IP, cmd.BindPort)

	var privilegedNamespacer, unprivilegedNamespacer uidgid.Namespacer

	if uidgid.Supported() {
		privilegedNamespacer = &uidgid.UidNamespacer{
			Translator: uidgid.NewTranslator(uidgid.NewPrivilegedMapper()),
			Logger:     logger.Session("uid-namespacer"),
		}

		unprivilegedNamespacer = &uidgid.UidNamespacer{
			Translator: uidgid.NewTranslator(uidgid.NewUnprivilegedMapper()),
			Logger:     logger.Session("uid-namespacer"),
		}
	} else {
		privilegedNamespacer = uidgid.NoopNamespacer{}
		unprivilegedNamespacer = uidgid.NoopNamespacer{}
	}

	locker := volume.NewLockManager()

	driver, err := cmd.driver(logger)
	if err != nil {
		logger.Error("failed-to-set-up-driver", err)
		return nil, err
	}

	filesystem, err := volume.NewFilesystem(driver, cmd.VolumesDir.Path())
	if err != nil {
		logger.Error("failed-to-initialize-filesystem", err)
		return nil, err
	}

	volumeRepo := volume.NewRepository(
		logger.Session("repository"),
		filesystem,
		locker,
		privilegedNamespacer,
		unprivilegedNamespacer,
	)

	apiHandler, err := api.NewHandler(
		logger.Session("api"),
		volume.NewStrategerizer(),
		volumeRepo,
	)
	if err != nil {
		logger.Fatal("failed-to-create-handler", err)
	}

	clock := clock.NewClock()

	morbidReality := reaper.NewReaper(clock, volumeRepo)

	members := []grouper.Member{
		{Name: "api", Runner: http_server.New(listenAddr, apiHandler)},
		{Name: "reaper", Runner: reaper.NewRunner(logger, clock, cmd.ReapInterval, morbidReality.Reap)},
		{Name: "debug-server", Runner: http_server.New(
			cmd.debugBindAddr(),
			http.DefaultServeMux,
		)},
	}

	return onReady(grouper.NewParallel(os.Interrupt, members), func() {
		logger.Info("listening", lager.Data{
			"addr": listenAddr,
		})
	}), nil
}

func (cmd *BaggageclaimCommand) constructLogger() (lager.Logger, *lager.ReconfigurableSink) {
	logger, reconfigurableSink := cmd.Logger.Logger("baggageclaim")

	if cmd.Metrics.YellerAPIKey != "" {
		yellerSink := zest.NewYellerSink(cmd.Metrics.YellerAPIKey, cmd.Metrics.YellerEnvironment)
		logger.RegisterSink(yellerSink)
	}

	return logger, reconfigurableSink
}

func onReady(runner ifrit.Runner, cb func()) ifrit.Runner {
	return ifrit.RunFunc(func(signals <-chan os.Signal, ready chan<- struct{}) error {
		process := ifrit.Background(runner)

		subExited := process.Wait()
		subReady := process.Ready()

		for {
			select {
			case <-subReady:
				cb()
				subReady = nil
			case err := <-subExited:
				return err
			case sig := <-signals:
				process.Signal(sig)
			}
		}
	})
}
<commit_msg>remove yeller configuration flags<commit_after>package baggageclaimcmd

import (
	"fmt"
	"net/http"
	"os"
	"time"

	"code.cloudfoundry.org/clock"
	"code.cloudfoundry.org/lager"
	"github.com/concourse/baggageclaim/api"
	"github.com/concourse/baggageclaim/reaper"
	"github.com/concourse/baggageclaim/uidgid"
	"github.com/concourse/baggageclaim/volume"
	"github.com/concourse/flag"
	"github.com/tedsuo/ifrit"
	"github.com/tedsuo/ifrit/grouper"
	"github.com/tedsuo/ifrit/http_server"
	"github.com/tedsuo/ifrit/sigmon"
)

type BaggageclaimCommand struct {
	Logger flag.Lager

	BindIP        flag.IP `long:"bind-ip"   default:"127.0.0.1" description:"IP address on which to listen for API traffic."`
	BindPort      uint16  `long:"bind-port" default:"7788"      description:"Port on which to listen for API traffic."`
	DebugBindPort uint16  `long:"bind-debug-port" default:"8099"    description:"Port on which to listen for baggageclaim pprof server."`

	VolumesDir flag.Dir `long:"volumes" required:"true" description:"Directory in which to place volume data."`

	Driver string `long:"driver" default:"detect" choice:"detect" choice:"naive" choice:"btrfs" choice:"overlay" description:"Driver to use for managing volumes."`

	BtrfsBin string `long:"btrfs-bin" default:"btrfs" description:"Path to btrfs binary"`
	MkfsBin  string `long:"mkfs-bin" default:"mkfs.btrfs" description:"Path to mkfs.btrfs binary"`

	OverlaysDir string `long:"overlays-dir" description:"Path to directory in which to store overlay data"`

	ReapInterval time.Duration `long:"reap-interval" default:"10s" description:"Interval on which to reap expired volumes."`
}

func (cmd *BaggageclaimCommand) debugBindAddr() string {
	return fmt.Sprintf("127.0.0.1:%d", cmd.DebugBindPort)
}

func (cmd *BaggageclaimCommand) Execute(args []string) error {
	runner, err := cmd.Runner(args)
	if err != nil {
		return err
	}

	return <-ifrit.Invoke(sigmon.New(runner)).Wait()
}

func (cmd *BaggageclaimCommand) Runner(args []string) (ifrit.Runner, error) {
	logger, _ := cmd.constructLogger()

	listenAddr := fmt.Sprintf("%s:%d", cmd.BindIP.IP, cmd.BindPort)

	var privilegedNamespacer, unprivilegedNamespacer uidgid.Namespacer

	if uidgid.Supported() {
		privilegedNamespacer = &uidgid.UidNamespacer{
			Translator: uidgid.NewTranslator(uidgid.NewPrivilegedMapper()),
			Logger:     logger.Session("uid-namespacer"),
		}

		unprivilegedNamespacer = &uidgid.UidNamespacer{
			Translator: uidgid.NewTranslator(uidgid.NewUnprivilegedMapper()),
			Logger:     logger.Session("uid-namespacer"),
		}
	} else {
		privilegedNamespacer = uidgid.NoopNamespacer{}
		unprivilegedNamespacer = uidgid.NoopNamespacer{}
	}

	locker := volume.NewLockManager()

	driver, err := cmd.driver(logger)
	if err != nil {
		logger.Error("failed-to-set-up-driver", err)
		return nil, err
	}

	filesystem, err := volume.NewFilesystem(driver, cmd.VolumesDir.Path())
	if err != nil {
		logger.Error("failed-to-initialize-filesystem", err)
		return nil, err
	}

	volumeRepo := volume.NewRepository(
		logger.Session("repository"),
		filesystem,
		locker,
		privilegedNamespacer,
		unprivilegedNamespacer,
	)

	apiHandler, err := api.NewHandler(
		logger.Session("api"),
		volume.NewStrategerizer(),
		volumeRepo,
	)
	if err != nil {
		logger.Fatal("failed-to-create-handler", err)
	}

	clock := clock.NewClock()

	morbidReality := reaper.NewReaper(clock, volumeRepo)

	members := []grouper.Member{
		{Name: "api", Runner: http_server.New(listenAddr, apiHandler)},
		{Name: "reaper", Runner: reaper.NewRunner(logger, clock, cmd.ReapInterval, morbidReality.Reap)},
		{Name: "debug-server", Runner: http_server.New(
			cmd.debugBindAddr(),
			http.DefaultServeMux,
		)},
	}

	return onReady(grouper.NewParallel(os.Interrupt, members), func() {
		logger.Info("listening", lager.Data{
			"addr": listenAddr,
		})
	}), nil
}

func (cmd *BaggageclaimCommand) constructLogger() (lager.Logger, *lager.ReconfigurableSink) {
	logger, reconfigurableSink := cmd.Logger.Logger("baggageclaim")

	return logger, reconfigurableSink
}

func onReady(runner ifrit.Runner, cb func()) ifrit.Runner {
	return ifrit.RunFunc(func(signals <-chan os.Signal, ready chan<- struct{}) error {
		process := ifrit.Background(runner)

		subExited := process.Wait()
		subReady := process.Ready()

		for {
			select {
			case <-subReady:
				cb()
				subReady = nil
			case err := <-subExited:
				return err
			case sig := <-signals:
				process.Signal(sig)
			}
		}
	})
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"errors"
	"fmt"
	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
	// "log"
	"github.com/go-martini/martini"
	"github.com/martini-contrib/cors"
	"github.com/martini-contrib/render"
	"os"
	"strings"
)

var session *mgo.Session
var collection *mgo.Collection
var err error

type Torrent struct {
	ID       bson.ObjectId `bson:"_id,omitempty"`
	Btih     string
	Title    []string
	Category []string
	Details  []string
	Download []string
}

func main() {
	// Try to connect to the database
	session, err = mgo.Dial("10.0.1.12")
	if err != nil {
		fmt.Println("Couldn't connect to Mongo. Please make sure it is installed and running.")
		return
	}
	defer session.Close()
	session.SetMode(mgo.Monotonic, true)
	collection = session.DB("bitcannon").C("torrents")
	collection.EnsureIndex(mgo.Index{Key: []string{"$text:title"}, Name: "title"})

	if len(os.Args) > 1 {
		importFile(os.Args[1])
	} else {
		runServer()
	}
}

func runServer() {
	fmt.Println("Starting the API server.")
	m := martini.Classic()
	m.Use(render.Renderer())
	m.Use(cors.Allow(&cors.Options{
		AllowAllOrigins: true,
		AllowMethods:    []string{"POST", "GET"},
		ExposeHeaders:   []string{"Content-Length"},
	}))
	m.Get("/stats", func(r render.Render) {
		count, err := collection.Count()
		if err != nil {
			r.JSON(500, map[string]interface{}{"count": "API Error"})
			return
		}
		r.JSON(200, map[string]interface{}{"count": count})
	})
	m.Get("/torrent/:btih", func(r render.Render, params martini.Params) {
		result := Torrent{}
		err = collection.Find(bson.M{"btih": params["btih"]}).One(&result)
		if err != nil {
			r.JSON(404, map[string]interface{}{"message": "Torrent not found."})
			return
		}
		r.JSON(200, result)
	})
	m.Get("/search/:query", func(r render.Render, params martini.Params) {
		result := []Torrent{}
		pipe := collection.Pipe([]bson.M{
			{"$match": bson.M{"$text": bson.M{"$search": params["query"]}}},
			{"$sort": bson.M{"score": bson.M{"$meta": "textScore"}}},
		})
		iter := pipe.Iter()
		err = iter.All(&result)
		if err != nil {
			r.JSON(404, map[string]interface{}{"message": err.Error()})
			return
		}
		r.JSON(200, result)
	})
	m.RunOnAddr(":1337")
}

func importFile(filename string) {
	fmt.Print("Attempting to parse ")
	fmt.Println(filename)

	file, err := os.Open(filename)
	if err != nil {
		fmt.Println("Error opening the file. Make sure it exists and is readable.")
		return
	}
	defer file.Close()

	scanner := bufio.NewScanner(file)
	imported := 0
	skipped := 0
	for scanner.Scan() {
		status, _ := importLine(scanner.Text())
		if status {
			imported++
		} else {
			skipped++
		}
	}

	fmt.Println("File parsing ended.")
	fmt.Print("New: ")
	fmt.Println(imported)
	fmt.Print("Dup: ")
	fmt.Println(skipped)

	if err := scanner.Err(); err != nil {
		fmt.Println("The program encountered an error while reading the file. Ensure that the file isn't corrupted.")
		return
	}
}

func importLine(line string) (bool, error) {
	if strings.Count(line, "|") != 4 {
		return false, errors.New("Something's up with this torrent. Expected 5 values separated by |.")
	}
	data := strings.Split(line, "|")
	return importHash(data[0], data[1], data[2], data[3], data[4])
}

// hash, title, category, details, download /*hash string, title string, category string, details string*/
func importHash(btih string, title string, category string, details string, download string) (bool, error) {
	err := collection.Insert(&Torrent{Btih: btih, Title: []string{title}, Category: []string{category}, Details: []string{details}, Download: []string{download}})
	if err != nil {
		return false, errors.New("Something went wrong when trying to insert.")
	}
	return true, nil
}
<commit_msg>Reuse the unique mongo id field to store btih. Closes #1.<commit_after>package main

import (
	"bufio"
	"errors"
	"fmt"
	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
	// "log"
	"github.com/go-martini/martini"
	"github.com/martini-contrib/cors"
	"github.com/martini-contrib/render"
	"os"
	"strings"
)

var session *mgo.Session
var collection *mgo.Collection
var err error

type Torrent struct {
	Btih     string `bson:"_id,omitempty"`
	Title    []string
	Category []string
	Details  []string
	Download []string
}

func main() {
	// Try to connect to the database
	session, err = mgo.Dial("10.0.1.12")
	if err != nil {
		fmt.Println("Couldn't connect to Mongo. Please make sure it is installed and running.")
		return
	}
	defer session.Close()
	session.SetMode(mgo.Monotonic, true)
	collection = session.DB("bitcannon").C("torrents")
	collection.EnsureIndex(mgo.Index{Key: []string{"$text:title"}, Name: "title"})

	if len(os.Args) > 1 {
		importFile(os.Args[1])
	} else {
		runServer()
	}
}

func runServer() {
	fmt.Println("Starting the API server.")
	m := martini.Classic()
	m.Use(render.Renderer())
	m.Use(cors.Allow(&cors.Options{
		AllowAllOrigins: true,
		AllowMethods:    []string{"POST", "GET"},
		ExposeHeaders:   []string{"Content-Length"},
	}))
	m.Get("/stats", func(r render.Render) {
		count, err := collection.Count()
		if err != nil {
			r.JSON(500, map[string]interface{}{"count": "API Error"})
			return
		}
		r.JSON(200, map[string]interface{}{"count": count})
	})
	m.Get("/torrent/:btih", func(r render.Render, params martini.Params) {
		result := Torrent{}
		err = collection.Find(bson.M{"btih": params["btih"]}).One(&result)
		if err != nil {
			r.JSON(404, map[string]interface{}{"message": "Torrent not found."})
			return
		}
		r.JSON(200, result)
	})
	m.Get("/search/:query", func(r render.Render, params martini.Params) {
		result := []Torrent{}
		pipe := collection.Pipe([]bson.M{
			{"$match": bson.M{"$text": bson.M{"$search": params["query"]}}},
			{"$sort": bson.M{"score": bson.M{"$meta": "textScore"}}},
		})
		iter := pipe.Iter()
		err = iter.All(&result)
		if err != nil {
			r.JSON(404, map[string]interface{}{"message": err.Error()})
			return
		}
		r.JSON(200, result)
	})
	m.RunOnAddr(":1337")
}

func importFile(filename string) {
	fmt.Print("Attempting to parse ")
	fmt.Println(filename)

	file, err := os.Open(filename)
	if err != nil {
		fmt.Println("Error opening the file. Make sure it exists and is readable.")
		return
	}
	defer file.Close()

	scanner := bufio.NewScanner(file)
	imported := 0
	skipped := 0
	for scanner.Scan() {
		status, _ := importLine(scanner.Text())
		if status {
			imported++
		} else {
			skipped++
		}
	}

	fmt.Println("File parsing ended.")
	fmt.Print("New: ")
	fmt.Println(imported)
	fmt.Print("Dup: ")
	fmt.Println(skipped)

	if err := scanner.Err(); err != nil {
		fmt.Println("The program encountered an error while reading the file. Ensure that the file isn't corrupted.")
		return
	}
}

func importLine(line string) (bool, error) {
	if strings.Count(line, "|") != 4 {
		return false, errors.New("Something's up with this torrent. Expected 5 values separated by |.")
	}
	data := strings.Split(line, "|")
	return importHash(data[0], data[1], data[2], data[3], data[4])
}

// hash, title, category, details, download /*hash string, title string, category string, details string*/
func importHash(btih string, title string, category string, details string, download string) (bool, error) {
	err := collection.Insert(&Torrent{Btih: btih, Title: []string{title}, Category: []string{category}, Details: []string{details}, Download: []string{download}})
	if err != nil {
		return false, errors.New("Something went wrong when trying to insert.")
	}
	return true, nil
}
<|endoftext|>


<commit_before>package v3

import (
	projectv3 "github.com/rancher/rancher/pkg/apis/project.cattle.io/v3"
)

var (
	ToolsSystemImages = struct {
		PipelineSystemImages projectv3.PipelineSystemImages
		AuthSystemImages     AuthSystemImages
	}{
		PipelineSystemImages: projectv3.PipelineSystemImages{
			Jenkins:       "rancher/pipeline-jenkins-server:v0.1.4",
			JenkinsJnlp:   "rancher/mirrored-jenkins-jnlp-slave:4.7-1",
			AlpineGit:     "rancher/pipeline-tools:v0.1.16",
			PluginsDocker: "rancher/mirrored-plugins-docker:19.03.8",
			Minio:         "rancher/mirrored-minio-minio:RELEASE.2020-07-13T18-09-56Z",
			Registry:      "registry:2",
			RegistryProxy: "rancher/pipeline-tools:v0.1.16",
			KubeApply:     "rancher/pipeline-tools:v0.1.16",
		},
		AuthSystemImages: AuthSystemImages{
			KubeAPIAuth: "rancher/kube-api-auth:v0.1.5",
		},
	}
)
<commit_msg>Bump kube-api-auth version<commit_after>package v3

import (
	projectv3 "github.com/rancher/rancher/pkg/apis/project.cattle.io/v3"
)

var (
	ToolsSystemImages = struct {
		PipelineSystemImages projectv3.PipelineSystemImages
		AuthSystemImages     AuthSystemImages
	}{
		PipelineSystemImages: projectv3.PipelineSystemImages{
			Jenkins:       "rancher/pipeline-jenkins-server:v0.1.4",
			JenkinsJnlp:   "rancher/mirrored-jenkins-jnlp-slave:4.7-1",
			AlpineGit:     "rancher/pipeline-tools:v0.1.16",
			PluginsDocker: "rancher/mirrored-plugins-docker:19.03.8",
			Minio:         "rancher/mirrored-minio-minio:RELEASE.2020-07-13T18-09-56Z",
			Registry:      "registry:2",
			RegistryProxy: "rancher/pipeline-tools:v0.1.16",
			KubeApply:     "rancher/pipeline-tools:v0.1.16",
		},
		AuthSystemImages: AuthSystemImages{
			KubeAPIAuth: "rancher/kube-api-auth:v0.1.6",
		},
	}
)
<|endoftext|>


<commit_before>package actions

import (
	"github.com/jetstack/navigator/pkg/apis/navigator/v1alpha1"
	"github.com/jetstack/navigator/pkg/controllers"
)

type ExcludeShards struct {
	// The Elasticsearch cluster resource being operated on
	Cluster *v1alpha1.ElasticsearchCluster
	// The node pool being scaled
	NodePool *v1alpha1.ElasticsearchClusterNodePool
	// Number of replicas to scale to
	Replicas int32
}

var _ controllers.Action = &ExcludeShards{}

func (c *ExcludeShards) Name() string {
	return "ExcludeShards"
}

func (c *ExcludeShards) Message() string {
	return ""
}

func (e *ExcludeShards) Execute(state *controllers.State) error {
	return nil
}
<commit_msg>Remove unimplemented exclude shards action<commit_after><|endoftext|>


<commit_before>package retention

import (
	"errors"
	"fmt"
	"strconv"
	"strings"
	"time"

	"github.com/cortexproject/cortex/pkg/chunk"
	"github.com/prometheus/common/model"

	"github.com/grafana/loki/pkg/storage"
)

const (
	chunkTimeRangeKeyV3   = '3'
	seriesRangeKeyV1      = '7'
	labelSeriesRangeKeyV1 = '8'
)

var ErrInvalidIndexKey = errors.New("invalid index key")

type InvalidIndexKeyError struct {
	HashKey  string
	RangeKey string
}

func newInvalidIndexKeyError(h, r []byte) InvalidIndexKeyError {
	return InvalidIndexKeyError{
		HashKey:  string(h),
		RangeKey: string(r),
	}
}

func (e InvalidIndexKeyError) Error() string {
	return fmt.Sprintf("%s: hash_key:%s range_key:%s", ErrInvalidIndexKey, e.HashKey, e.RangeKey)
}

func (e InvalidIndexKeyError) Is(target error) bool {
	return target == ErrInvalidIndexKey
}

type ChunkRef struct {
	UserID   []byte
	SeriesID []byte
	ChunkID  []byte
	From     model.Time
	Through  model.Time
}

func (c ChunkRef) String() string {
	return fmt.Sprintf("UserID: %s , SeriesID: %s , Time: [%s,%s]", c.UserID, c.SeriesID, c.From, c.Through)
}

func parseChunkRef(hashKey, rangeKey []byte) (ChunkRef, bool, error) {
	componentsRef := getComponents()
	defer putComponents(componentsRef)
	components := componentsRef.components

	components = decodeRangeKey(rangeKey, components)
	if len(components) == 0 {
		return ChunkRef{}, false, newInvalidIndexKeyError(hashKey, rangeKey)
	}

	keyType := components[len(components)-1]
	if len(keyType) == 0 || keyType[0] != chunkTimeRangeKeyV3 {
		return ChunkRef{}, false, nil
	}
	chunkID := components[len(components)-2]

	userID, hexFrom, hexThrough, ok := parseChunkID(chunkID)
	if !ok {
		return ChunkRef{}, false, newInvalidIndexKeyError(hashKey, rangeKey)
	}
	from, err := strconv.ParseInt(unsafeGetString(hexFrom), 16, 64)
	if err != nil {
		return ChunkRef{}, false, err
	}
	through, err := strconv.ParseInt(unsafeGetString(hexThrough), 16, 64)
	if err != nil {
		return ChunkRef{}, false, err
	}

	return ChunkRef{
		UserID:   userID,
		SeriesID: seriesFromHash(hashKey),
		From:     model.Time(from),
		Through:  model.Time(through),
		ChunkID:  chunkID,
	}, true, nil
}

func parseChunkID(chunkID []byte) (userID []byte, hexFrom, hexThrough []byte, valid bool) {
	var (
		j, i int
		hex  []byte
	)

	for j < len(chunkID) {
		if chunkID[j] != '/' {
			j++
			continue
		}
		userID = chunkID[:j]
		hex = chunkID[j+1:]
		break
	}
	if len(userID) == 0 {
		return nil, nil, nil, false
	}
	_, i = readOneHexPart(hex)
	if i == 0 {
		return nil, nil, nil, false
	}
	hex = hex[i+1:]
	hexFrom, i = readOneHexPart(hex)
	if i == 0 {
		return nil, nil, nil, false
	}
	hex = hex[i+1:]
	hexThrough, i = readOneHexPart(hex)
	if i == 0 {
		return nil, nil, nil, false
	}
	return userID, hexFrom, hexThrough, true
}

func readOneHexPart(hex []byte) (part []byte, i int) {
	for i < len(hex) {
		if hex[i] != ':' {
			i++
			continue
		}
		return hex[:i], i
	}
	return nil, 0
}

func parseLabelIndexSeriesID(hashKey, rangeKey []byte) ([]byte, bool, error) {
	componentsRef := getComponents()
	defer putComponents(componentsRef)
	components := componentsRef.components
	var seriesID []byte
	components = decodeRangeKey(rangeKey, components)
	if len(components) < 4 {
		return nil, false, newInvalidIndexKeyError(hashKey, rangeKey)
	}
	keyType := components[len(components)-1]
	if len(keyType) == 0 {
		return nil, false, nil
	}
	switch keyType[0] {
	case labelSeriesRangeKeyV1:
		seriesID = components[1]
	case seriesRangeKeyV1:
		seriesID = components[0]
	default:
		return nil, false, nil
	}
	return seriesID, true, nil
}

type LabelSeriesRangeKey struct {
	SeriesID []byte
	UserID   []byte
	Name     []byte
}

func (l LabelSeriesRangeKey) String() string {
	return fmt.Sprintf("%s:%s:%s", l.SeriesID, l.UserID, l.Name)
}

func parseLabelSeriesRangeKey(hashKey, rangeKey []byte) (LabelSeriesRangeKey, bool, error) {
	rangeComponentsRef := getComponents()
	defer putComponents(rangeComponentsRef)
	rangeComponents := rangeComponentsRef.components
	hashComponentsRef := getComponents()
	defer putComponents(hashComponentsRef)
	hashComponents := hashComponentsRef.components

	rangeComponents = decodeRangeKey(rangeKey, rangeComponents)
	if len(rangeComponents) < 4 {
		return LabelSeriesRangeKey{}, false, newInvalidIndexKeyError(hashKey, rangeKey)
	}
	keyType := rangeComponents[len(rangeComponents)-1]
	if len(keyType) == 0 || keyType[0] != labelSeriesRangeKeyV1 {
		return LabelSeriesRangeKey{}, false, nil
	}
	hashComponents = splitBytesBy(hashKey, ':', hashComponents)
	// 	> v10		HashValue:  fmt.Sprintf("%02d:%s:%s:%s", shard, bucket.hashKey , metricName, v.Name),
	// < v10		HashValue:  fmt.Sprintf("%s:%s:%s", bucket.hashKey, metricName, v.Name),

	if len(hashComponents) < 4 {
		return LabelSeriesRangeKey{}, false, newInvalidIndexKeyError(hashKey, rangeKey)
	}
	return LabelSeriesRangeKey{
		SeriesID: rangeComponents[1],
		Name:     hashComponents[len(hashComponents)-1],
		UserID:   hashComponents[len(hashComponents)-4],
	}, true, nil
}

func validatePeriods(config storage.SchemaConfig) error {
	for _, schema := range config.Configs {
		if schema.IndexTables.Period != 24*time.Hour {
			return fmt.Errorf("schema period must be daily, was: %s", schema.IndexTables.Period)
		}
	}
	return nil
}

func schemaPeriodForTable(config storage.SchemaConfig, tableName string) (chunk.PeriodConfig, bool) {
	// first round removes configs that does not have the prefix.
	candidates := []chunk.PeriodConfig{}
	for _, schema := range config.Configs {
		if strings.HasPrefix(tableName, schema.IndexTables.Prefix) {
			candidates = append(candidates, schema)
		}
	}
	// WARN we  assume period is always daily. This is only true for boltdb-shipper.
	var (
		matched chunk.PeriodConfig
		found   bool
	)
	for _, schema := range candidates {
		periodIndex, err := strconv.ParseInt(strings.TrimPrefix(tableName, schema.IndexTables.Prefix), 10, 64)
		if err != nil {
			continue
		}
		periodSec := int64(schema.IndexTables.Period / time.Second)
		tableTs := model.TimeFromUnix(periodIndex * periodSec)
		if tableTs.After(schema.From.Time) || tableTs == schema.From.Time {
			matched = schema
			found = true
		}
	}

	return matched, found
}

func seriesFromHash(h []byte) (seriesID []byte) {
	var index int
	for i := range h {
		if h[i] == ':' {
			index++
		}
		if index == 2 {
			seriesID = h[i+1:]
			return
		}
	}
	return
}

// decodeKey decodes hash and range value from a boltdb key.
func decodeKey(k []byte) (hashValue, rangeValue []byte) {
	// hashValue + 0 + string(rangeValue)
	for i := range k {
		if k[i] == 0 {
			hashValue = k[:i]
			rangeValue = k[i+1:]
			return
		}
	}
	return
}

func splitBytesBy(value []byte, by byte, components [][]byte) [][]byte {
	components = components[:0]
	i, j := 0, 0
	for j < len(value) {
		if value[j] != by {
			j++
			continue
		}
		components = append(components, value[i:j])
		j++
		i = j
	}
	components = append(components, value[i:])
	return components
}

func decodeRangeKey(value []byte, components [][]byte) [][]byte {
	components = components[:0]
	i, j := 0, 0
	for j < len(value) {
		if value[j] != 0 {
			j++
			continue
		}
		components = append(components, value[i:j])
		j++
		i = j
	}
	return components
}
<commit_msg>just log a warning when a store type other than boltdb-shipper is detected when custom retention is enabled (#3785)<commit_after>package retention

import (
	"errors"
	"fmt"
	"strconv"
	"strings"
	"time"

	"github.com/prometheus/common/model"

	"github.com/cortexproject/cortex/pkg/chunk"
	util_log "github.com/cortexproject/cortex/pkg/util/log"
	"github.com/go-kit/kit/log/level"

	"github.com/grafana/loki/pkg/storage"
	"github.com/grafana/loki/pkg/storage/stores/shipper"
)

const (
	chunkTimeRangeKeyV3   = '3'
	seriesRangeKeyV1      = '7'
	labelSeriesRangeKeyV1 = '8'
)

var ErrInvalidIndexKey = errors.New("invalid index key")

type InvalidIndexKeyError struct {
	HashKey  string
	RangeKey string
}

func newInvalidIndexKeyError(h, r []byte) InvalidIndexKeyError {
	return InvalidIndexKeyError{
		HashKey:  string(h),
		RangeKey: string(r),
	}
}

func (e InvalidIndexKeyError) Error() string {
	return fmt.Sprintf("%s: hash_key:%s range_key:%s", ErrInvalidIndexKey, e.HashKey, e.RangeKey)
}

func (e InvalidIndexKeyError) Is(target error) bool {
	return target == ErrInvalidIndexKey
}

type ChunkRef struct {
	UserID   []byte
	SeriesID []byte
	ChunkID  []byte
	From     model.Time
	Through  model.Time
}

func (c ChunkRef) String() string {
	return fmt.Sprintf("UserID: %s , SeriesID: %s , Time: [%s,%s]", c.UserID, c.SeriesID, c.From, c.Through)
}

func parseChunkRef(hashKey, rangeKey []byte) (ChunkRef, bool, error) {
	componentsRef := getComponents()
	defer putComponents(componentsRef)
	components := componentsRef.components

	components = decodeRangeKey(rangeKey, components)
	if len(components) == 0 {
		return ChunkRef{}, false, newInvalidIndexKeyError(hashKey, rangeKey)
	}

	keyType := components[len(components)-1]
	if len(keyType) == 0 || keyType[0] != chunkTimeRangeKeyV3 {
		return ChunkRef{}, false, nil
	}
	chunkID := components[len(components)-2]

	userID, hexFrom, hexThrough, ok := parseChunkID(chunkID)
	if !ok {
		return ChunkRef{}, false, newInvalidIndexKeyError(hashKey, rangeKey)
	}
	from, err := strconv.ParseInt(unsafeGetString(hexFrom), 16, 64)
	if err != nil {
		return ChunkRef{}, false, err
	}
	through, err := strconv.ParseInt(unsafeGetString(hexThrough), 16, 64)
	if err != nil {
		return ChunkRef{}, false, err
	}

	return ChunkRef{
		UserID:   userID,
		SeriesID: seriesFromHash(hashKey),
		From:     model.Time(from),
		Through:  model.Time(through),
		ChunkID:  chunkID,
	}, true, nil
}

func parseChunkID(chunkID []byte) (userID []byte, hexFrom, hexThrough []byte, valid bool) {
	var (
		j, i int
		hex  []byte
	)

	for j < len(chunkID) {
		if chunkID[j] != '/' {
			j++
			continue
		}
		userID = chunkID[:j]
		hex = chunkID[j+1:]
		break
	}
	if len(userID) == 0 {
		return nil, nil, nil, false
	}
	_, i = readOneHexPart(hex)
	if i == 0 {
		return nil, nil, nil, false
	}
	hex = hex[i+1:]
	hexFrom, i = readOneHexPart(hex)
	if i == 0 {
		return nil, nil, nil, false
	}
	hex = hex[i+1:]
	hexThrough, i = readOneHexPart(hex)
	if i == 0 {
		return nil, nil, nil, false
	}
	return userID, hexFrom, hexThrough, true
}

func readOneHexPart(hex []byte) (part []byte, i int) {
	for i < len(hex) {
		if hex[i] != ':' {
			i++
			continue
		}
		return hex[:i], i
	}
	return nil, 0
}

func parseLabelIndexSeriesID(hashKey, rangeKey []byte) ([]byte, bool, error) {
	componentsRef := getComponents()
	defer putComponents(componentsRef)
	components := componentsRef.components
	var seriesID []byte
	components = decodeRangeKey(rangeKey, components)
	if len(components) < 4 {
		return nil, false, newInvalidIndexKeyError(hashKey, rangeKey)
	}
	keyType := components[len(components)-1]
	if len(keyType) == 0 {
		return nil, false, nil
	}
	switch keyType[0] {
	case labelSeriesRangeKeyV1:
		seriesID = components[1]
	case seriesRangeKeyV1:
		seriesID = components[0]
	default:
		return nil, false, nil
	}
	return seriesID, true, nil
}

type LabelSeriesRangeKey struct {
	SeriesID []byte
	UserID   []byte
	Name     []byte
}

func (l LabelSeriesRangeKey) String() string {
	return fmt.Sprintf("%s:%s:%s", l.SeriesID, l.UserID, l.Name)
}

func parseLabelSeriesRangeKey(hashKey, rangeKey []byte) (LabelSeriesRangeKey, bool, error) {
	rangeComponentsRef := getComponents()
	defer putComponents(rangeComponentsRef)
	rangeComponents := rangeComponentsRef.components
	hashComponentsRef := getComponents()
	defer putComponents(hashComponentsRef)
	hashComponents := hashComponentsRef.components

	rangeComponents = decodeRangeKey(rangeKey, rangeComponents)
	if len(rangeComponents) < 4 {
		return LabelSeriesRangeKey{}, false, newInvalidIndexKeyError(hashKey, rangeKey)
	}
	keyType := rangeComponents[len(rangeComponents)-1]
	if len(keyType) == 0 || keyType[0] != labelSeriesRangeKeyV1 {
		return LabelSeriesRangeKey{}, false, nil
	}
	hashComponents = splitBytesBy(hashKey, ':', hashComponents)
	// 	> v10		HashValue:  fmt.Sprintf("%02d:%s:%s:%s", shard, bucket.hashKey , metricName, v.Name),
	// < v10		HashValue:  fmt.Sprintf("%s:%s:%s", bucket.hashKey, metricName, v.Name),

	if len(hashComponents) < 4 {
		return LabelSeriesRangeKey{}, false, newInvalidIndexKeyError(hashKey, rangeKey)
	}
	return LabelSeriesRangeKey{
		SeriesID: rangeComponents[1],
		Name:     hashComponents[len(hashComponents)-1],
		UserID:   hashComponents[len(hashComponents)-4],
	}, true, nil
}

func validatePeriods(config storage.SchemaConfig) error {
	for _, schema := range config.Configs {
		if schema.IndexType != shipper.BoltDBShipperType {
			level.Warn(util_log.Logger).Log("msg", fmt.Sprintf("custom retention is not supported for store %s, no retention will be applied for schema entry with start date %s", schema.IndexType, schema.From))
			continue
		}
		if schema.IndexTables.Period != 24*time.Hour {
			return fmt.Errorf("schema period must be daily, was: %s", schema.IndexTables.Period)
		}
	}
	return nil
}

func schemaPeriodForTable(config storage.SchemaConfig, tableName string) (chunk.PeriodConfig, bool) {
	// first round removes configs that does not have the prefix.
	candidates := []chunk.PeriodConfig{}
	for _, schema := range config.Configs {
		if strings.HasPrefix(tableName, schema.IndexTables.Prefix) {
			candidates = append(candidates, schema)
		}
	}
	// WARN we  assume period is always daily. This is only true for boltdb-shipper.
	var (
		matched chunk.PeriodConfig
		found   bool
	)
	for _, schema := range candidates {
		periodIndex, err := strconv.ParseInt(strings.TrimPrefix(tableName, schema.IndexTables.Prefix), 10, 64)
		if err != nil {
			continue
		}
		periodSec := int64(schema.IndexTables.Period / time.Second)
		tableTs := model.TimeFromUnix(periodIndex * periodSec)
		if tableTs.After(schema.From.Time) || tableTs == schema.From.Time {
			matched = schema
			found = true
		}
	}

	return matched, found
}

func seriesFromHash(h []byte) (seriesID []byte) {
	var index int
	for i := range h {
		if h[i] == ':' {
			index++
		}
		if index == 2 {
			seriesID = h[i+1:]
			return
		}
	}
	return
}

// decodeKey decodes hash and range value from a boltdb key.
func decodeKey(k []byte) (hashValue, rangeValue []byte) {
	// hashValue + 0 + string(rangeValue)
	for i := range k {
		if k[i] == 0 {
			hashValue = k[:i]
			rangeValue = k[i+1:]
			return
		}
	}
	return
}

func splitBytesBy(value []byte, by byte, components [][]byte) [][]byte {
	components = components[:0]
	i, j := 0, 0
	for j < len(value) {
		if value[j] != by {
			j++
			continue
		}
		components = append(components, value[i:j])
		j++
		i = j
	}
	components = append(components, value[i:])
	return components
}

func decodeRangeKey(value []byte, components [][]byte) [][]byte {
	components = components[:0]
	i, j := 0, 0
	for j < len(value) {
		if value[j] != 0 {
			j++
			continue
		}
		components = append(components, value[i:j])
		j++
		i = j
	}
	return components
}
<|endoftext|>


<commit_before>/*-
Copyright (c) 2016, Jörg Pernfuß <joerg.pernfuss@1und1.de>
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this
  list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

package main

import (
	"encoding/hex"
	"sync"
	"time"

	"github.com/mjolnir42/scrypth64"
	"github.com/satori/go.uuid"
)

// supervisor internal storage format for tokens
type svToken struct {
	validFrom    time.Time
	expiresAt    time.Time
	binToken     []byte
	binExpiresAt []byte
	salt         []byte
}

// read/write locked map of tokens
type svTokenMap struct {
	// token(hex.string) -> svToken
	TMap  map[string]svToken
	mutex sync.RWMutex
}

func (t *svTokenMap) read(token string) *svToken {
	t.rlock()
	defer t.runlock()
	if tok, ok := t.TMap[token]; ok {
		return &tok
	}
	return nil
}

func (t *svTokenMap) insert(token, valid, expires, salt string) error {
	var (
		err                     error
		valTime, expTime        time.Time
		bExpTime, bSalt, bToken []byte
	)
	// convert input data into the different formats required to
	// perform later actions without conversions
	if valTime, err = time.Parse(rfc3339Milli, valid); err != nil {
		return err
	}
	if expTime, err = time.Parse(rfc3339Milli, expires); err != nil {
		return err
	}
	if bExpTime, err = expTime.MarshalBinary(); err != nil {
		return err
	}
	if bToken, err = hex.DecodeString(token); err != nil {
		return err
	}
	if bSalt, err = hex.DecodeString(salt); err != nil {
		return err
	}
	// whiteout unstable subsecond timestamp part with "random" value
	copy(bExpTime[9:], []byte{0xde, 0xad, 0xca, 0xfe})
	// acquire write lock
	t.lock()
	defer t.unlock()

	// insert token
	t.TMap[token] = svToken{
		validFrom:    valTime,
		expiresAt:    expTime,
		binToken:     bToken,
		binExpiresAt: bExpTime,
		salt:         bSalt,
	}
	return nil
}

// set writelock
func (t *svTokenMap) lock() {
	t.mutex.Lock()
}

// set readlock
func (t *svTokenMap) rlock() {
	t.mutex.RLock()
}

// release writelock
func (t *svTokenMap) unlock() {
	t.mutex.Unlock()
}

// release readlock
func (t *svTokenMap) runlock() {
	t.mutex.RUnlock()
}

// supervisor internal storage format for credentials
type svCredential struct {
	id          uuid.UUID
	validFrom   time.Time
	expiresAt   time.Time
	cryptMCF    scrypth64.Mcf
	resetActive bool
	resetToken  string
	isActive    bool
}

type svCredMap struct {
	// username -> svCredential
	CMap  map[string]svCredential
	mutex sync.RWMutex
}

func (c *svCredMap) read(user string) *svCredential {
	c.rlock()
	defer c.runlock()
	if cred, ok := c.CMap[user]; ok {
		return &cred
	}
	return nil
}

func (c *svCredMap) insert(user string, uid uuid.UUID, valid, expires time.Time, mcf scrypth64.Mcf) {
	c.lock()
	defer c.unlock()
	c.CMap[user] = svCredential{
		id:          uid,
		validFrom:   valid,
		expiresAt:   expires,
		cryptMCF:    mcf,
		resetActive: false,
		resetToken:  "",
		isActive:    true,
	}
}

// set writelock
func (c *svCredMap) lock() {
	c.mutex.Lock()
}

// set readlock
func (c *svCredMap) rlock() {
	c.mutex.RLock()
}

// release writelock
func (c *svCredMap) unlock() {
	c.mutex.Unlock()
}

// release readlock
func (c *svCredMap) runlock() {
	c.mutex.RUnlock()
}

//
//
// read/write locked map of key exchanges
type svKexMap struct {
	// kexid(uuid.string) -> auth.Kex
	KMap  map[string]auth.Kex
	mutex sync.RWMutex
}

// the nonce information would normally mean returning
// a copy is problematic, but since these keys are only
// used for any client/server exchange, they are never
// put back
func (k *svKexMap) read(kexRequest string) *auth.Kex {
	k.rlock()
	defer k.runlock()
	if kex, ok := k.KMap[kexRequest]; ok {
		return &kex
	}
	return nil
}

func (k *svKexMap) insert(kex auth.Kex) {
	k.lock()
	defer k.unlock()

	k.KMap[kex.Request.String()] = kex
}

func (k *svKexMap) remove(kexRequest string) {
	k.lock()
	defer k.unlock()

	delete(k.KMap, kexRequest)
}

// set writelock
func (k *svKexMap) lock() {
	k.mutex.Lock()
}

// set readlock
func (k *svKexMap) rlock() {
	k.mutex.RLock()
}

// release writelock
func (k *svKexMap) unlock() {
	k.mutex.Unlock()
}

// release readlock
func (k *svKexMap) runlock() {
	k.mutex.RUnlock()
}

// vim: ts=4 sw=4 sts=4 noet fenc=utf-8 ffs=unix
<commit_msg>Remove resetToken from credentials map<commit_after>/*-
Copyright (c) 2016, Jörg Pernfuß <joerg.pernfuss@1und1.de>
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this
  list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

package main

import (
	"encoding/hex"
	"sync"
	"time"

	"github.com/mjolnir42/scrypth64"
	"github.com/satori/go.uuid"
)

// supervisor internal storage format for tokens
type svToken struct {
	validFrom    time.Time
	expiresAt    time.Time
	binToken     []byte
	binExpiresAt []byte
	salt         []byte
}

// read/write locked map of tokens
type svTokenMap struct {
	// token(hex.string) -> svToken
	TMap  map[string]svToken
	mutex sync.RWMutex
}

func (t *svTokenMap) read(token string) *svToken {
	t.rlock()
	defer t.runlock()
	if tok, ok := t.TMap[token]; ok {
		return &tok
	}
	return nil
}

func (t *svTokenMap) insert(token, valid, expires, salt string) error {
	var (
		err                     error
		valTime, expTime        time.Time
		bExpTime, bSalt, bToken []byte
	)
	// convert input data into the different formats required to
	// perform later actions without conversions
	if valTime, err = time.Parse(rfc3339Milli, valid); err != nil {
		return err
	}
	if expTime, err = time.Parse(rfc3339Milli, expires); err != nil {
		return err
	}
	if bExpTime, err = expTime.MarshalBinary(); err != nil {
		return err
	}
	if bToken, err = hex.DecodeString(token); err != nil {
		return err
	}
	if bSalt, err = hex.DecodeString(salt); err != nil {
		return err
	}
	// whiteout unstable subsecond timestamp part with "random" value
	copy(bExpTime[9:], []byte{0xde, 0xad, 0xca, 0xfe})
	// acquire write lock
	t.lock()
	defer t.unlock()

	// insert token
	t.TMap[token] = svToken{
		validFrom:    valTime,
		expiresAt:    expTime,
		binToken:     bToken,
		binExpiresAt: bExpTime,
		salt:         bSalt,
	}
	return nil
}

// set writelock
func (t *svTokenMap) lock() {
	t.mutex.Lock()
}

// set readlock
func (t *svTokenMap) rlock() {
	t.mutex.RLock()
}

// release writelock
func (t *svTokenMap) unlock() {
	t.mutex.Unlock()
}

// release readlock
func (t *svTokenMap) runlock() {
	t.mutex.RUnlock()
}

// supervisor internal storage format for credentials
type svCredential struct {
	id          uuid.UUID
	validFrom   time.Time
	expiresAt   time.Time
	cryptMCF    scrypth64.Mcf
	resetActive bool
	isActive    bool
}

type svCredMap struct {
	// username -> svCredential
	CMap  map[string]svCredential
	mutex sync.RWMutex
}

func (c *svCredMap) read(user string) *svCredential {
	c.rlock()
	defer c.runlock()
	if cred, ok := c.CMap[user]; ok {
		return &cred
	}
	return nil
}

func (c *svCredMap) insert(user string, uid uuid.UUID, valid, expires time.Time, mcf scrypth64.Mcf) {
	c.lock()
	defer c.unlock()
	c.CMap[user] = svCredential{
		id:          uid,
		validFrom:   valid,
		expiresAt:   expires,
		cryptMCF:    mcf,
		resetActive: false,
		isActive:    true,
	}
}

// set writelock
func (c *svCredMap) lock() {
	c.mutex.Lock()
}

// set readlock
func (c *svCredMap) rlock() {
	c.mutex.RLock()
}

// release writelock
func (c *svCredMap) unlock() {
	c.mutex.Unlock()
}

// release readlock
func (c *svCredMap) runlock() {
	c.mutex.RUnlock()
}

//
//
// read/write locked map of key exchanges
type svKexMap struct {
	// kexid(uuid.string) -> auth.Kex
	KMap  map[string]auth.Kex
	mutex sync.RWMutex
}

// the nonce information would normally mean returning
// a copy is problematic, but since these keys are only
// used for any client/server exchange, they are never
// put back
func (k *svKexMap) read(kexRequest string) *auth.Kex {
	k.rlock()
	defer k.runlock()
	if kex, ok := k.KMap[kexRequest]; ok {
		return &kex
	}
	return nil
}

func (k *svKexMap) insert(kex auth.Kex) {
	k.lock()
	defer k.unlock()

	k.KMap[kex.Request.String()] = kex
}

func (k *svKexMap) remove(kexRequest string) {
	k.lock()
	defer k.unlock()

	delete(k.KMap, kexRequest)
}

// set writelock
func (k *svKexMap) lock() {
	k.mutex.Lock()
}

// set readlock
func (k *svKexMap) rlock() {
	k.mutex.RLock()
}

// release writelock
func (k *svKexMap) unlock() {
	k.mutex.Unlock()
}

// release readlock
func (k *svKexMap) runlock() {
	k.mutex.RUnlock()
}

// vim: ts=4 sw=4 sts=4 noet fenc=utf-8 ffs=unix
<|endoftext|>


<commit_before>/*
   Copyright 2010-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.

   This file is licensed under the Apache License, Version 2.0 (the "License").
   You may not use this file except in compliance with the License. A copy of
   the License is located at

    http://aws.amazon.com/apache2.0/

   This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied. See the License for the
   specific language governing permissions and limitations under the License.
*/

package main

import (
    "github.com/aws/aws-sdk-go/aws"
    "github.com/aws/aws-sdk-go/aws/session"
    "github.com/aws/aws-sdk-go/service/s3"
    "fmt"
    "os"
)

// Allows person with EMAIL address PERMISSION access to BUCKET
// If PERMISSION is missing, they get READ access.
//
// Usage:
//    go run s3_put_bucket_acl.go BUCKET EMAIL [PERMISSION]
func main() {
    if len(os.Args) < 3 {
        exitErrorf("Bucket name and email address required; permission optional (READ if omitted)\nUsage: go run", os.Args[0], "BUCKET EMAIL [PERMISSION]")
    }

    bucket := os.Args[1]
    address := os.Args[2]

    permission := "READ"

    if len(os.Args) == 4 {
        permission = os.Args[3]

        if !(permission == "FULL_CONTROL" || permission == "WRITE" || permission == "WRITE_ACP" || permission == "READ" || permission == "READ_ACP") {
            fmt.Println("Illegal permission value. It must be one of:")
            fmt.Println("FULL_CONTROL, WRITE, WRITE_ACP, READ, or READ_ACP")
            os.Exit(1)

}
    }

    userType := "AmazonCustomerByEmail"

    // Initialize a session that loads credentials from the shared credentials file ~/.aws/credentials
    // and the region from the shared configuratin file ~/.aws/config.
    sess := session.Must(session.NewSessionWithOptions(session.Options{
        SharedConfigState: session.SharedConfigEnable,
    }))

    // Create S3 service client
    svc := s3.New(sess)

    // Get existing ACL
    result, err := svc.GetBucketAcl(&s3.GetBucketAclInput{Bucket: &bucket})
    if err != nil {
        exitErrorf(err.Error())
    }

    owner := *result.Owner.DisplayName
    ownerId := *result.Owner.ID

    // Existing grants
    grants := result.Grants

    // Create new grantee to add to grants
    var newGrantee = s3.Grantee{EmailAddress: &address, Type: &userType}
    var newGrant = s3.Grant{Grantee: &newGrantee, Permission: &permission}

    // Add them to the grants
    grants = append(grants, &newGrant)

    params := &s3.PutBucketAclInput{
        Bucket: &bucket,
        AccessControlPolicy: &s3.AccessControlPolicy{
            Grants: grants,
            Owner: &s3.Owner{
                DisplayName: &owner,
                ID:          &ownerId,
            },
        },
    }

    // Set bucket ACL
    _, err = svc.PutBucketAcl(params)
    if err != nil {
        exitErrorf(err.Error())
    }

    fmt.Println("Congratulations. You gave user with email address", address, permission, "permission to bucket", bucket)
}

func exitErrorf(msg string, args ...interface{}) {
    fmt.Fprintf(os.Stderr, msg+"\n", args...)
    os.Exit(1)
}
<commit_msg>Fixed S3 ACL example in Go<commit_after>/*
   Copyright 2010-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.

   This file is licensed under the Apache License, Version 2.0 (the "License").
   You may not use this file except in compliance with the License. A copy of
   the License is located at

    http://aws.amazon.com/apache2.0/

   This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied. See the License for the
   specific language governing permissions and limitations under the License.
*/

package main

import (
    "github.com/aws/aws-sdk-go/aws"
    "github.com/aws/aws-sdk-go/aws/session"
    "github.com/aws/aws-sdk-go/service/s3"
    "fmt"
    "os"
)

// Allows person with EMAIL address PERMISSION access to BUCKET
// If PERMISSION is missing, they get READ access.
//
// Usage:
//    go run s3_put_bucket_acl.go BUCKET EMAIL [PERMISSION]
func main() {
    if len(os.Args) < 3 {
        exitErrorf("Bucket name and email address required; permission optional (READ if omitted)\nUsage: go run", os.Args[0], "BUCKET EMAIL [PERMISSION]")
    }

    bucket := os.Args[1]
    address := os.Args[2]

    permission := "READ"

    if len(os.Args) == 4 {
        permission = os.Args[3]

        if !(permission == "FULL_CONTROL" || permission == "WRITE" || permission == "WRITE_ACP" || permission == "READ" || permission == "READ_ACP") {
            fmt.Println("Illegal permission value. It must be one of:")
            fmt.Println("FULL_CONTROL, WRITE, WRITE_ACP, READ, or READ_ACP")
            os.Exit(1)

        }
    }

    userType := "AmazonCustomerByEmail"

    // Initialize a session that loads credentials from the shared credentials file ~/.aws/credentials
    // and the region from the shared configuratin file ~/.aws/config.
    sess := session.Must(session.NewSessionWithOptions(session.Options{
        SharedConfigState: session.SharedConfigEnable,
    }))

    // Create S3 service client
    svc := s3.New(sess)

    // Get existing ACL
    result, err := svc.GetBucketAcl(&s3.GetBucketAclInput{Bucket: &bucket})
    if err != nil {
        exitErrorf(err.Error())
    }

    owner := *result.Owner.DisplayName
    ownerId := *result.Owner.ID

    // Existing grants
    grants := result.Grants

    // Create new grantee to add to grants
    var newGrantee = s3.Grantee{EmailAddress: &address, Type: &userType}
    var newGrant = s3.Grant{Grantee: &newGrantee, Permission: &permission}

    // Add them to the grants
    grants = append(grants, &newGrant)

    params := &s3.PutBucketAclInput{
        Bucket: &bucket,
        AccessControlPolicy: &s3.AccessControlPolicy{
            Grants: grants,
            Owner: &s3.Owner{
                DisplayName: &owner,
                ID:          &ownerId,
            },
        },
    }

    // Set bucket ACL
    _, err = svc.PutBucketAcl(params)
    if err != nil {
        exitErrorf(err.Error())
    }

    fmt.Println("Congratulations. You gave user with email address", address, permission, "permission to bucket", bucket)
}

func exitErrorf(msg string, args ...interface{}) {
    fmt.Fprintf(os.Stderr, msg+"\n", args...)
    os.Exit(1)
}
<|endoftext|>