<commit_before>package cache

import (
	"bytes"
	"compress/zlib"
	"errors"
	"fmt"
	"gnd.la/cache/driver"
	"gnd.la/config"
	"gnd.la/encoding/codec"
	"gnd.la/log"
	"io"
	"reflect"
	"strconv"
	"strings"
)

var (
	ErrNotFound = errors.New("item not found in cache")
	imports     = map[string]string{
		"memcache": "gnd.la/cache/driver/memcache",
		"redis":    "gnd.la/cache/driver/redis",
	}
)

type Cache struct {
	// The Logger to log debug messages and, more importantly, errors.
	// New() initialies the Logger to log.Std.
	Logger            *log.Logger
	prefix            string
	prefixLen         int
	driver            driver.Driver
	codec             *codec.Codec
	minCompressLength int
	compressLevel     int
	numQueries        int
}

func (c *Cache) backendKey(key string) string {
	if c.prefixLen > 0 {
		return c.prefix + key
	}
	return key
}

func (c *Cache) frontendKey(key string) string {
	return key[c.prefixLen:]
}

// Set stores the given object in the cache associated with the
// given key. Timeout is the number of seconds until the item
// expires. If the timeout is 0, the item never expires, but
// might be only purged from cache when running out of space.
func (c *Cache) Set(key string, object interface{}, timeout int) error {
	b, err := c.codec.Encode(object)
	if err != nil {
		eerr := &cacheError{
			op:    "encoding object",
			key:   key,
			codec: true,
			err:   err,
		}
		c.error(eerr)
		return eerr
	}
	return c.SetBytes(key, b, timeout)
}

// Get retrieves the requested item from the cache and decodes it
// into the passed interface{}, which must be addressable.
// If the item is not found in the cache, ErrNotFound is returned.
// Other errors mean that either there was a problem communicating
// with the cache or the item could not be decoded.
func (c *Cache) Get(key string, obj interface{}) error {
	b, err := c.GetBytes(key)
	if err != nil {
		return err
	}
	cerr := c.codec.Decode(b, obj)
	if cerr != nil {
		derr := &cacheError{
			op:    "decoding object",
			key:   key,
			codec: true,
			err:   cerr,
		}
		c.error(derr)
		return derr
	}
	return nil
}

// GetMulti returns several objects as a map[string]interface{}
// in only one roundtrip to the cache. The obj parameter is used only
// to initialize the objects before they're passed to the codec for decoding,
// since not all codecs include the object type in its serialization (e.g. JSON).
// If you're using a codec which encodes the object type (e.g. Gob) or you want
// to decode the objects into a raw interface{} you might pass nil as the second
// parameter to this function.
func (c *Cache) GetMulti(keys []string, obj interface{}) (map[string]interface{}, error) {
	c.numQueries++
	if c.prefixLen > 0 {
		k := make([]string, len(keys))
		for ii, v := range keys {
			k[ii] = c.backendKey(v)
		}
		keys = k
	}
	data, err := c.driver.GetMulti(keys)
	if err != nil {
		gerr := &cacheError{
			op:  "getting multiple keys",
			key: strings.Join(keys, ", "),
			err: err,
		}
		c.error(gerr)
		return nil, gerr
	}
	objects := make(map[string]interface{}, len(data))
	var typ *reflect.Type
	if obj != nil {
		t := reflect.TypeOf(obj)
		for t.Kind() == reflect.Ptr {
			t = t.Elem()
		}
		typ = &t
	}
	for k, v := range data {
		var object interface{}
		if typ != nil {
			p := reflect.New(*typ)
			err = c.codec.Decode(v, p.Interface())
			object = p.Elem().Interface()
		} else {
			err = c.codec.Decode(v, &object)
		}
		if err != nil {
			derr := &cacheError{
				op:  "decoding object",
				key: k,
				err: err,
			}
			c.error(derr)
			return nil, derr
		}
		objects[c.frontendKey(k)] = object
	}
	return objects, nil
}

// SetBytes stores the given byte array assocciated with
// the given key. See the documentation for Set for an
// explanation of the timeout parameter
func (c *Cache) SetBytes(key string, b []byte, timeout int) error {
	c.numQueries++
	if c.minCompressLength >= 0 {
		if l := len(b); l > c.minCompressLength {
			var buf bytes.Buffer
			w, err := zlib.NewWriterLevel(&buf, c.compressLevel)
			if err == nil {
				w.Write(b)
				w.Close()
				cb := buf.Bytes()
				if cl := len(cb); cl < l {
					b = cb
					c.debugf("Compressed key %s from %d bytes to %d bytes", key, l, cl)
				}
			} else {
				c.warningf("error opening zlib writer: %s", err)
			}
		}
	}
	k := c.backendKey(key)
	err := c.driver.Set(k, b, timeout)
	if err != nil {
		serr := &cacheError{
			op:  "setting key",
			key: key,
			err: err,
		}
		c.error(serr)
		return serr
	}
	c.debugf("Set key %s (%d bytes), expiring in %d", k, len(b), timeout)
	return nil
}

// GetBytes returns the byte array assocciated with the given key
func (c *Cache) GetBytes(key string) ([]byte, error) {
	c.numQueries++
	b, err := c.driver.Get(c.backendKey(key))
	if err != nil {
		gerr := &cacheError{
			op:  "getting key",
			key: key,
			err: err,
		}
		c.error(gerr)
		return nil, gerr
	}
	if b == nil {
		return nil, ErrNotFound
	}
	if c.minCompressLength >= 0 && len(b) > 0 {
		r, err := zlib.NewReader(bytes.NewBuffer(b))
		if err == nil {
			var buf bytes.Buffer
			_, err = io.Copy(&buf, r)
			if err == nil {
				if r.Close() == nil {
					b = buf.Bytes()
				}
			} else {
				r.Close()
			}
		}
	}
	return b, nil
}

// Delete removes the key from the cache. An error is returned only
// if the item was found but couldn't be deleted. Deleting a non-existant
// item is always successful.
func (c *Cache) Delete(key string) error {
	c.numQueries++
	err := c.driver.Delete(c.backendKey(key))
	if err != nil {
		derr := &cacheError{
			op:  "deleting",
			key: key,
			err: err,
		}
		c.error(derr)
		return derr
	}
	return nil
}

// NumQueries returns the number of queries made to this
// cache since it was initialized.
func (c *Cache) NumQueries() int {
	return c.numQueries
}

// Close closes the cache connection. If you're using a cache
// using mux.Context helper methods, the cache will be closed
// for you.
func (c *Cache) Close() error {
	return c.driver.Close()
}

// Connection returns a interface{} wrapping the native connection
// type for the cache client (e.g. a memcache or redis connection).
// Some drivers might return a nil connection (like the fs or the
// dummy driver).
func (c *Cache) Connection() interface{} {
	return c.driver.Connection()
}

func (c *Cache) debugf(format string, arg ...interface{}) {
	if c.Logger != nil {
		c.Logger.Debugf(format, arg...)
	}
}

func (c *Cache) warningf(format string, arg ...interface{}) {
	if c.Logger != nil {
		c.Logger.Warningf(format, arg...)
	}
}

func (c *Cache) error(err *cacheError) {
	if c.Logger != nil {
		c.Logger.Error(err)
	}
}

func newConfig(conf *config.URL) (*Cache, error) {
	if conf.Options == nil {
		conf.Options = config.Options{}
	}
	cache := &Cache{
		Logger:            log.Std,
		minCompressLength: -1,
		compressLevel:     zlib.DefaultCompression,
	}

	if codecName := conf.Get("codec"); codecName != "" {
		cache.codec = codec.Get(codecName)
		if cache.codec == nil {
			if imp := codec.RequiredImport(codecName); imp != "" {
				return nil, fmt.Errorf("please import %q to use the codec %q", imp, codecName)
			}
			return nil, fmt.Errorf("unknown codec %q, maybe you forgot an import?", codecName)
		}
	} else {
		cache.codec = codec.Get("gob")
	}

	cache.prefix = conf.Get("prefix")
	cache.prefixLen = len(cache.prefix)

	if mcl := conf.Get("min_compress"); mcl != "" {
		val, err := strconv.Atoi(mcl)
		if err != nil {
			return nil, fmt.Errorf("invalid min_compress value %q (%s) (must be an integer)", mcl, err)
		}
		cache.minCompressLength = val
	}

	if cl := conf.Get("compress_level"); cl != "" {
		val, err := strconv.Atoi(cl)
		if err != nil {
			return nil, fmt.Errorf("invalid compress_level %q (%s) (must be an integer)", cl, err)
		}
		if (val < zlib.NoCompression || val > zlib.BestCompression) && val != -1 {
			return nil, fmt.Errorf("invalid compress_level %d (must be -1 or between %d and %d)",
				val, zlib.NoCompression, zlib.BestCompression)
		}
		cache.compressLevel = val
	}
	var opener driver.Opener
	if conf.Scheme != "" {
		opener = driver.Get(conf.Scheme)
		if opener == nil {
			if imp := imports[conf.Scheme]; imp != "" {
				return nil, fmt.Errorf("please import %q to use the cache driver %q", imp, conf.Scheme)
			}
			return nil, fmt.Errorf("unknown cache driver %q, maybe you forgot an import?", conf.Scheme)
		}
	} else {
		opener = driver.Get("dummy")
	}
	var err error
	if cache.driver, err = opener(conf.Value, conf.Options); err != nil {
		return nil, err
	}
	return cache, nil
}

// New returns a new cache instance, using the given
// configuration URL. If the configuration is nil, a
// dummy cache is returned, which always returns that the
// object does not exists in the cache.
func New(url *config.URL) (*Cache, error) {
	if url == nil {
		// Use dummy cache
		url = &config.URL{}
	}
	return newConfig(url)
}
<commit_msg>Allow the cache to use pipes, rather than having explicit zlib support<commit_after>package cache

import (
	"errors"
	"fmt"
	"gnd.la/cache/driver"
	"gnd.la/config"
	"gnd.la/encoding/codec"
	"gnd.la/encoding/pipe"
	"gnd.la/log"
	"reflect"
	"strings"
)

var (
	ErrNotFound = errors.New("item not found in cache")
	imports     = map[string]string{
		"memcache": "gnd.la/cache/driver/memcache",
		"redis":    "gnd.la/cache/driver/redis",
	}
)

type Cache struct {
	// The Logger to log debug messages and, more importantly, errors.
	// New() initialies the Logger to log.Std.
	Logger     *log.Logger
	prefix     string
	prefixLen  int
	driver     driver.Driver
	codec      *codec.Codec
	pipe       *pipe.Pipe
	numQueries int
}

func (c *Cache) backendKey(key string) string {
	if c.prefixLen > 0 {
		return c.prefix + key
	}
	return key
}

func (c *Cache) frontendKey(key string) string {
	return key[c.prefixLen:]
}

// Set stores the given object in the cache associated with the
// given key. Timeout is the number of seconds until the item
// expires. If the timeout is 0, the item never expires, but
// might be only purged from cache when running out of space.
func (c *Cache) Set(key string, object interface{}, timeout int) error {
	b, err := c.codec.Encode(object)
	if err != nil {
		eerr := &cacheError{
			op:    "encoding object",
			key:   key,
			codec: true,
			err:   err,
		}
		c.error(eerr)
		return eerr
	}
	return c.SetBytes(key, b, timeout)
}

// Get retrieves the requested item from the cache and decodes it
// into the passed interface{}, which must be addressable.
// If the item is not found in the cache, ErrNotFound is returned.
// Other errors mean that either there was a problem communicating
// with the cache or the item could not be decoded.
func (c *Cache) Get(key string, obj interface{}) error {
	b, err := c.GetBytes(key)
	if err != nil {
		return err
	}
	cerr := c.codec.Decode(b, obj)
	if cerr != nil {
		derr := &cacheError{
			op:    "decoding object",
			key:   key,
			codec: true,
			err:   cerr,
		}
		c.error(derr)
		return derr
	}
	return nil
}

// GetMulti returns several objects as a map[string]interface{}
// in only one roundtrip to the cache. The obj parameter is used only
// to initialize the objects before they're passed to the codec for decoding,
// since not all codecs include the object type in its serialization (e.g. JSON).
// If you're using a codec which encodes the object type (e.g. Gob) or you want
// to decode the objects into a raw interface{} you might pass nil as the second
// parameter to this function.
func (c *Cache) GetMulti(keys []string, obj interface{}) (map[string]interface{}, error) {
	c.numQueries++
	if c.prefixLen > 0 {
		k := make([]string, len(keys))
		for ii, v := range keys {
			k[ii] = c.backendKey(v)
		}
		keys = k
	}
	data, err := c.driver.GetMulti(keys)
	if err != nil {
		gerr := &cacheError{
			op:  "getting multiple keys",
			key: strings.Join(keys, ", "),
			err: err,
		}
		c.error(gerr)
		return nil, gerr
	}
	objects := make(map[string]interface{}, len(data))
	var typ *reflect.Type
	if obj != nil {
		t := reflect.TypeOf(obj)
		for t.Kind() == reflect.Ptr {
			t = t.Elem()
		}
		typ = &t
	}
	for k, v := range data {
		var object interface{}
		if typ != nil {
			p := reflect.New(*typ)
			err = c.codec.Decode(v, p.Interface())
			object = p.Elem().Interface()
		} else {
			err = c.codec.Decode(v, &object)
		}
		if err != nil {
			derr := &cacheError{
				op:  "decoding object",
				key: k,
				err: err,
			}
			c.error(derr)
			return nil, derr
		}
		objects[c.frontendKey(k)] = object
	}
	return objects, nil
}

// SetBytes stores the given byte array assocciated with
// the given key. See the documentation for Set for an
// explanation of the timeout parameter
func (c *Cache) SetBytes(key string, b []byte, timeout int) error {
	c.numQueries++
	if c.pipe != nil {
		var err error
		b, err = c.pipe.Encode(b)
		if err != nil {
			perr := &cacheError{
				op:  "encoding data with pipe",
				key: key,
				err: err,
			}
			c.error(perr)
			return perr
		}
	}
	k := c.backendKey(key)
	err := c.driver.Set(k, b, timeout)
	if err != nil {
		serr := &cacheError{
			op:  "setting key",
			key: key,
			err: err,
		}
		c.error(serr)
		return serr
	}
	c.debugf("Set key %s (%d bytes), expiring in %d", k, len(b), timeout)
	return nil
}

// GetBytes returns the byte array assocciated with the given key
func (c *Cache) GetBytes(key string) ([]byte, error) {
	c.numQueries++
	b, err := c.driver.Get(c.backendKey(key))
	if err != nil {
		gerr := &cacheError{
			op:  "getting key",
			key: key,
			err: err,
		}
		c.error(gerr)
		return nil, gerr
	}
	if b == nil {
		return nil, ErrNotFound
	}
	if c.pipe != nil {
		b, err = c.pipe.Decode(b)
		if err != nil {
			perr := &cacheError{
				op:  "decoding data with pipe",
				key: key,
				err: err,
			}
			c.error(perr)
			return nil, perr
		}
	}
	return b, nil
}

// Delete removes the key from the cache. An error is returned only
// if the item was found but couldn't be deleted. Deleting a non-existant
// item is always successful.
func (c *Cache) Delete(key string) error {
	c.numQueries++
	err := c.driver.Delete(c.backendKey(key))
	if err != nil {
		derr := &cacheError{
			op:  "deleting",
			key: key,
			err: err,
		}
		c.error(derr)
		return derr
	}
	return nil
}

// NumQueries returns the number of queries made to this
// cache since it was initialized.
func (c *Cache) NumQueries() int {
	return c.numQueries
}

// Close closes the cache connection. If you're using a cache
// using mux.Context helper methods, the cache will be closed
// for you.
func (c *Cache) Close() error {
	return c.driver.Close()
}

// Connection returns a interface{} wrapping the native connection
// type for the cache client (e.g. a memcache or redis connection).
// Some drivers might return a nil connection (like the fs or the
// dummy driver).
func (c *Cache) Connection() interface{} {
	return c.driver.Connection()
}

func (c *Cache) debugf(format string, arg ...interface{}) {
	if c.Logger != nil {
		c.Logger.Debugf(format, arg...)
	}
}

func (c *Cache) warningf(format string, arg ...interface{}) {
	if c.Logger != nil {
		c.Logger.Warningf(format, arg...)
	}
}

func (c *Cache) error(err *cacheError) {
	if c.Logger != nil {
		c.Logger.Error(err)
	}
}

func newConfig(conf *config.URL) (*Cache, error) {
	if conf.Options == nil {
		conf.Options = config.Options{}
	}
	cache := &Cache{
		Logger: log.Std,
	}

	if codecName := conf.Get("codec"); codecName != "" {
		cache.codec = codec.Get(codecName)
		if cache.codec == nil {
			if imp := codec.RequiredImport(codecName); imp != "" {
				return nil, fmt.Errorf("please import %q to use the codec %q", imp, codecName)
			}
			return nil, fmt.Errorf("unknown codec %q, maybe you forgot an import?", codecName)
		}
	} else {
		cache.codec = codec.Get("gob")
	}

	cache.prefix = conf.Get("prefix")
	cache.prefixLen = len(cache.prefix)
	if pipeName := conf.Get("pipe"); pipeName != "" {
		cache.pipe = pipe.Get(pipeName)
		if cache.pipe == nil {
			if imp := pipe.RequiredImport(pipeName); imp != "" {
				return nil, fmt.Errorf("please import %q to use the pipe %q", imp, pipeName)
			}
			return nil, fmt.Errorf("unknown pipe %q, maybe you forgot an import?", pipeName)
		}
	}
	var opener driver.Opener
	if conf.Scheme != "" {
		opener = driver.Get(conf.Scheme)
		if opener == nil {
			if imp := imports[conf.Scheme]; imp != "" {
				return nil, fmt.Errorf("please import %q to use the cache driver %q", imp, conf.Scheme)
			}
			return nil, fmt.Errorf("unknown cache driver %q, maybe you forgot an import?", conf.Scheme)
		}
	} else {
		opener = driver.Get("dummy")
	}
	var err error
	if cache.driver, err = opener(conf.Value, conf.Options); err != nil {
		return nil, err
	}
	return cache, nil
}

// New returns a new cache instance, using the given
// configuration URL. If the configuration is nil, a
// dummy cache is returned, which always returns that the
// object does not exists in the cache.
func New(url *config.URL) (*Cache, error) {
	if url == nil {
		// Use dummy cache
		url = &config.URL{}
	}
	return newConfig(url)
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"io/ioutil"
	"net/http"
	"testing"
	"os"

	"github.com/stretchr/testify/assert"
	"github.com/vkuznet/transfer2go/core"
	"github.com/vkuznet/transfer2go/utils"
)

var url = "http://localhost:8989"

type tests struct {
	description        string
	url                string
	expectedStatusCode int
	expectedBody       string
	result             bool
}

// Function names are according to api endpoints

func TestStatus(t *testing.T) {
	assert := assert.New(t)

	test := tests{
		description:        "Check status of server",
		url:                url + "/status",
		expectedStatusCode: 200,
		expectedBody:       "Test",
	}

	var data map[string]interface{}

	resp, err := http.Get(test.url)
	actual, err := ioutil.ReadAll(resp.Body)

	defer resp.Body.Close()
	json.Unmarshal([]byte(actual), &data)
	assert.NoError(err)

	assert.Equal(test.expectedStatusCode, resp.StatusCode, test.description)
	assert.Equal(test.expectedBody, data["name"], test.description)

}

func TestAgents(t *testing.T) {
	assert := assert.New(t)

	test := tests{
		description:        "Test the list of connected agents",
		url:                url + "/agents",
		expectedStatusCode: 200,
		expectedBody:       "http://localhost:8989",
	}

	var data map[string]interface{}

	resp, err := http.Get(test.url)
	actual, err := ioutil.ReadAll(resp.Body)

	defer resp.Body.Close()
	json.Unmarshal([]byte(actual), &data)
	assert.NoError(err)

	assert.Equal(test.expectedStatusCode, resp.StatusCode, test.description)
	assert.Equal(test.expectedBody, data["Test"], test.description)

}

func TestTFC(t *testing.T) {
	assert := assert.New(t)

	test := tests{
		description:        "Check TFC upload functionality",
		url:                url + "/tfc",
		expectedStatusCode: 200,
		expectedBody:       "http://localhost:8989",
	}

	f, err := os.Create("data/testdata.txt")
	d2 := []byte{115, 111, 109, 101, 10}
	_, err = f.Write(d2)
	assert.NoError(err)
	f.Close()

	fname := "data/records.json"
	c, err := ioutil.ReadFile(fname)
	assert.NoError(err)

	var records []core.CatalogEntry
	err = json.Unmarshal([]byte(c), &records)
	assert.NoError(err)

	d, err := json.Marshal(records)
	assert.NoError(err)

	des := url + "/tfc"
	resp := utils.FetchResponse(des, d)

	assert.Equal(test.expectedStatusCode, resp.StatusCode, test.description)

	err = os.Remove("data/testdata.txt")
	assert.NoError(err)

}
<commit_msg>Add tfc read test<commit_after>package main

import (
	"encoding/json"
	"io/ioutil"
	"net/http"
	"os"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/vkuznet/transfer2go/core"
	"github.com/vkuznet/transfer2go/utils"
)

var url = "http://localhost:8989"

type tests struct {
	description        string
	url                string
	expectedStatusCode int
	expectedBody       string
	result             bool
}

// Function names are according to api endpoints

func TestStatus(t *testing.T) {
	assert := assert.New(t)

	test := tests{
		description:        "Check status of server",
		url:                url + "/status",
		expectedStatusCode: 200,
		expectedBody:       "Test",
	}

	var data map[string]interface{}

	resp, err := http.Get(test.url)
	actual, err := ioutil.ReadAll(resp.Body)

	defer resp.Body.Close()
	json.Unmarshal([]byte(actual), &data)
	assert.NoError(err)

	assert.Equal(test.expectedStatusCode, resp.StatusCode, test.description)
	assert.Equal(test.expectedBody, data["name"], test.description)

}

func TestAgents(t *testing.T) {
	assert := assert.New(t)

	test := tests{
		description:        "Test the list of connected agents",
		url:                url + "/agents",
		expectedStatusCode: 200,
		expectedBody:       "http://localhost:8989",
	}

	var data map[string]interface{}

	resp, err := http.Get(test.url)
	actual, err := ioutil.ReadAll(resp.Body)

	defer resp.Body.Close()
	json.Unmarshal([]byte(actual), &data)
	assert.NoError(err)

	assert.Equal(test.expectedStatusCode, resp.StatusCode, test.description)
	assert.Equal(test.expectedBody, data["Test"], test.description)

}

func TestWriteTFC(t *testing.T) {
	assert := assert.New(t)

	test := tests{
		description:        "Check TFC upload functionality",
		url:                url + "/tfc",
		expectedStatusCode: 200,
		expectedBody:       "http://localhost:8989",
	}

	f, err := os.Create("data/testdata.txt")
	d2 := []byte{115, 111, 109, 101, 10}
	_, err = f.Write(d2)
	assert.NoError(err)
	f.Close()

	fname := "data/records.json"
	c, err := ioutil.ReadFile(fname)
	assert.NoError(err)

	var records []core.CatalogEntry
	err = json.Unmarshal([]byte(c), &records)
	assert.NoError(err)

	d, err := json.Marshal(records)
	assert.NoError(err)

	des := url + "/tfc"
	resp := utils.FetchResponse(des, d)

	assert.Equal(test.expectedStatusCode, resp.StatusCode, test.description)

	err = os.Remove("data/testdata.txt")
	assert.NoError(err)

}

func TestFiles(t *testing.T) {
	assert := assert.New(t)

	test := tests{
		description:        "Get the list of files",
		url:                url + "/files",
		expectedStatusCode: 200,
		expectedBody:       "[\"/store/file.root\"]",
	}

	resp, err := http.Get(test.url)
	actual, err := ioutil.ReadAll(resp.Body)
	assert.NoError(err)
	defer resp.Body.Close()

	assert.Equal(test.expectedStatusCode, resp.StatusCode, test.description)
	assert.Equal(test.expectedBody, string(actual), test.description)

}

func TestReadTFC(t *testing.T) {
	assert := assert.New(t)

	test := tests{
		description:        "Get TFC records",
		url:                url + "/tfc",
		expectedStatusCode: 200,
		expectedBody:       "test/data/testdata.txt",
	}

	var data []map[string]interface{}

	resp, err := http.Get(test.url)
	actual, err := ioutil.ReadAll(resp.Body)

	defer resp.Body.Close()
	json.Unmarshal([]byte(actual), &data)
	assert.NoError(err)

	assert.Equal(test.expectedStatusCode, resp.StatusCode, test.description)
	assert.Equal(test.expectedBody, data[0]["pfn"], test.description)

}
<|endoftext|>


<commit_before>package repo

import "todolist/spi"

// Creates the appropriate repository depending on the application configuration
func NewRepo() spi.Repo {
	r := NewInMemoryRepo()
	r.Init()
	return r
}<commit_msg>Rely on the ELASTICSEARCH_URL env variable<commit_after>package repo

import (
	"todolist/spi"
	"os"
	"errors"
)

var (
	esUrl = os.Getenv("ELASTICSEARCH_URL")
	esLogin = os.Getenv("ELASTICSEARCH_USERNAME")
	esPwd = os.Getenv("ELASTICSEARCH_PASSWORD")
)

// Creates the appropriate repository depending on the application configuration:
// - set the environment variable ELASTICSEARCH_URL to back the application with ElasticSearch
// - leave the variable unset to let the application use an in-memory repository
func NewRepo() spi.Repo {
	if "" == esUrl {
		r := NewInMemoryRepo()
		r.Init()
		return r
	}

	panic(errors.New("elasticsearch repo not implemented yet"))
}<|endoftext|>


<commit_before>// Ellipsis objects

package py

type EllipsisType struct{}

var (
	EllipsisTypeType = NewType("EllipsisType", "")
	Ellipsis         = EllipsisType(struct{}{})
)

// Type of this object
func (s EllipsisType) Type() *Type {
	return EllipsisTypeType
}

func (a EllipsisType) M__bool__() Object {
	return False
}

func (a EllipsisType) M__str__() Object {
	return String("Ellipsis")
}

// Check interface is satisfied
var _ I__bool__ = Ellipsis
var _ I__str__ = Ellipsis
<commit_msg>py: implement __eq__ and __ne__ for Ellipsis<commit_after>// Ellipsis objects

package py

type EllipsisType struct{}

var (
	EllipsisTypeType = NewType("EllipsisType", "")
	Ellipsis         = EllipsisType(struct{}{})
)

// Type of this object
func (s EllipsisType) Type() *Type {
	return EllipsisTypeType
}

func (a EllipsisType) M__bool__() Object {
	return False
}

func (a EllipsisType) M__str__() Object {
	return String("Ellipsis")
}

// Check interface is satisfied
var _ I__bool__ = Ellipsis
var _ I__str__ = Ellipsis

func (a EllipsisType) M__eq__(other Object) Object {
	if _, ok := other.(EllipsisType); ok {
		return True
	}
	return False
}

func (a EllipsisType) M__ne__(other Object) Object {
	if _, ok := other.(EllipsisType); ok {
		return False
	}
	return True
}
<|endoftext|>


<commit_before>package main

import (
	"sort"
	"strconv"
	"sync"
	"time"

	"github.com/lxc/lxd/lxd/db"
	"github.com/lxc/lxd/lxd/state"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/logger"

	log "github.com/lxc/lxd/shared/log15"
)

var containersCmd = Command{
	name: "containers",
	get:  containersGet,
	post: containersPost,
}

var containerCmd = Command{
	name:   "containers/{name}",
	get:    containerGet,
	put:    containerPut,
	delete: containerDelete,
	post:   containerPost,
	patch:  containerPatch,
}

var containerStateCmd = Command{
	name: "containers/{name}/state",
	get:  containerState,
	put:  containerStatePut,
}

var containerFileCmd = Command{
	name:   "containers/{name}/files",
	get:    containerFileHandler,
	post:   containerFileHandler,
	delete: containerFileHandler,
}

var containerSnapshotsCmd = Command{
	name: "containers/{name}/snapshots",
	get:  containerSnapshotsGet,
	post: containerSnapshotsPost,
}

var containerSnapshotCmd = Command{
	name:   "containers/{name}/snapshots/{snapshotName}",
	get:    snapshotHandler,
	post:   snapshotHandler,
	delete: snapshotHandler,
}

var containerConsoleCmd = Command{
	name:   "containers/{name}/console",
	get:    containerConsoleLogGet,
	post:   containerConsolePost,
	delete: containerConsoleLogDelete,
}

var containerExecCmd = Command{
	name: "containers/{name}/exec",
	post: containerExecPost,
}

var containerMetadataCmd = Command{
	name: "containers/{name}/metadata",
	get:  containerMetadataGet,
	put:  containerMetadataPut,
}

var containerMetadataTemplatesCmd = Command{
	name:   "containers/{name}/metadata/templates",
	get:    containerMetadataTemplatesGet,
	post:   containerMetadataTemplatesPostPut,
	put:    containerMetadataTemplatesPostPut,
	delete: containerMetadataTemplatesDelete,
}

var containerBackupsCmd = Command{
	name: "containers/{name}/backups",
	get:  containerBackupsGet,
	post: containerBackupsPost,
}

var containerBackupCmd = Command{
	name:   "containers/{name}/backups/{backupName}",
	get:    containerBackupGet,
	post:   containerBackupPost,
	delete: containerBackupDelete,
}

var containerBackupExportCmd = Command{
	name: "containers/{name}/backups/{backupName}/export",
	get:  containerBackupExportGet,
}

type containerAutostartList []container

func (slice containerAutostartList) Len() int {
	return len(slice)
}

func (slice containerAutostartList) Less(i, j int) bool {
	iOrder := slice[i].ExpandedConfig()["boot.autostart.priority"]
	jOrder := slice[j].ExpandedConfig()["boot.autostart.priority"]

	if iOrder != jOrder {
		iOrderInt, _ := strconv.Atoi(iOrder)
		jOrderInt, _ := strconv.Atoi(jOrder)
		return iOrderInt > jOrderInt
	}

	return slice[i].Name() < slice[j].Name()
}

func (slice containerAutostartList) Swap(i, j int) {
	slice[i], slice[j] = slice[j], slice[i]
}

func containersRestart(s *state.State) error {
	// Get all the containers
	result, err := s.Cluster.ContainersList(db.CTypeRegular)
	if err != nil {
		return err
	}

	containers := []container{}

	for _, name := range result {
		c, err := containerLoadByName(s, name)
		if err != nil {
			return err
		}

		containers = append(containers, c)
	}

	sort.Sort(containerAutostartList(containers))

	// Restart the containers
	for _, c := range containers {
		config := c.ExpandedConfig()
		lastState := config["volatile.last_state.power"]

		autoStart := config["boot.autostart"]
		autoStartDelay := config["boot.autostart.delay"]

		if shared.IsTrue(autoStart) || (autoStart == "" && lastState == "RUNNING") {
			if c.IsRunning() {
				continue
			}

			err = c.Start(false)
			if err != nil {
				logger.Errorf("Failed to start container '%s': %v", c.Name(), err)
			}

			autoStartDelayInt, err := strconv.Atoi(autoStartDelay)
			if err == nil {
				time.Sleep(time.Duration(autoStartDelayInt) * time.Second)
			}
		}
	}

	return nil
}

type containerStopList []container

func (slice containerStopList) Len() int {
	return len(slice)
}

func (slice containerStopList) Less(i, j int) bool {
	iOrder := slice[i].ExpandedConfig()["boot.stop.priority"]
	jOrder := slice[j].ExpandedConfig()["boot.stop.priority"]

	if iOrder != jOrder {
		iOrderInt, _ := strconv.Atoi(iOrder)
		jOrderInt, _ := strconv.Atoi(jOrder)
		return iOrderInt > jOrderInt // check this line (prob <)
	}

	return slice[i].Name() < slice[j].Name()
}

func (slice containerStopList) Swap(i, j int) {
	slice[i], slice[j] = slice[j], slice[i]
}

func containersShutdown(s *state.State) error {
	var wg sync.WaitGroup

	// Get all the containers
	results, err := s.Cluster.ContainersList(db.CTypeRegular)
	if err != nil {
		return err
	}

	containers := []container{}

	for _, name := range results {
		c, err := containerLoadByName(s, name)
		if err != nil {
			return err
		}

		containers = append(containers, c)
	}

	sort.Sort(containerStopList(containers))

	// Reset all container states
	err = s.Cluster.ContainersResetState()
	if err != nil {
		return err
	}

	var lastPriority int = 0

	if len(containers) != 0 {
		lastPriority, _ = strconv.Atoi(containers[0].ExpandedConfig()["boot.stop.priority"])
	}

	for _, c := range containers {
		priority, _ := strconv.Atoi(c.ExpandedConfig()["boot.stop.priority"])

		// Enforce shutdown priority
		if priority != lastPriority {
			lastPriority = priority

			// Wait for containers with higher priority to finish
			wg.Wait()
		}

		// Record the current state
		lastState := c.State()

		// Stop the container
		if lastState != "BROKEN" && lastState != "STOPPED" {
			// Determinate how long to wait for the container to shutdown cleanly
			var timeoutSeconds int
			value, ok := c.ExpandedConfig()["boot.host_shutdown_timeout"]
			if ok {
				timeoutSeconds, _ = strconv.Atoi(value)
			} else {
				timeoutSeconds = 30
			}

			// Stop the container
			wg.Add(1)
			go func(c container, lastState string) {
				c.Shutdown(time.Second * time.Duration(timeoutSeconds))
				c.Stop(false)
				c.ConfigKeySet("volatile.last_state.power", lastState)

				wg.Done()
			}(c, lastState)
		} else {
			c.ConfigKeySet("volatile.last_state.power", lastState)
		}
	}
	wg.Wait()

	return nil
}

func containerDeleteSnapshots(s *state.State, cname string) error {
	results, err := s.Cluster.ContainerGetSnapshots(cname)
	if err != nil {
		return err
	}

	for _, sname := range results {
		sc, err := containerLoadByName(s, sname)
		if err != nil {
			logger.Error(
				"containerDeleteSnapshots: Failed to load the snapshotcontainer",
				log.Ctx{"container": cname, "snapshot": sname})

			continue
		}

		if err := sc.Delete(); err != nil {
			logger.Error(
				"containerDeleteSnapshots: Failed to delete a snapshotcontainer",
				log.Ctx{"container": cname, "snapshot": sname, "err": err})
		}
	}

	return nil
}
<commit_msg>lxd/cluster: Only restart local containers<commit_after>package main

import (
	"sort"
	"strconv"
	"sync"
	"time"

	"github.com/lxc/lxd/lxd/db"
	"github.com/lxc/lxd/lxd/state"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/logger"

	log "github.com/lxc/lxd/shared/log15"
)

var containersCmd = Command{
	name: "containers",
	get:  containersGet,
	post: containersPost,
}

var containerCmd = Command{
	name:   "containers/{name}",
	get:    containerGet,
	put:    containerPut,
	delete: containerDelete,
	post:   containerPost,
	patch:  containerPatch,
}

var containerStateCmd = Command{
	name: "containers/{name}/state",
	get:  containerState,
	put:  containerStatePut,
}

var containerFileCmd = Command{
	name:   "containers/{name}/files",
	get:    containerFileHandler,
	post:   containerFileHandler,
	delete: containerFileHandler,
}

var containerSnapshotsCmd = Command{
	name: "containers/{name}/snapshots",
	get:  containerSnapshotsGet,
	post: containerSnapshotsPost,
}

var containerSnapshotCmd = Command{
	name:   "containers/{name}/snapshots/{snapshotName}",
	get:    snapshotHandler,
	post:   snapshotHandler,
	delete: snapshotHandler,
}

var containerConsoleCmd = Command{
	name:   "containers/{name}/console",
	get:    containerConsoleLogGet,
	post:   containerConsolePost,
	delete: containerConsoleLogDelete,
}

var containerExecCmd = Command{
	name: "containers/{name}/exec",
	post: containerExecPost,
}

var containerMetadataCmd = Command{
	name: "containers/{name}/metadata",
	get:  containerMetadataGet,
	put:  containerMetadataPut,
}

var containerMetadataTemplatesCmd = Command{
	name:   "containers/{name}/metadata/templates",
	get:    containerMetadataTemplatesGet,
	post:   containerMetadataTemplatesPostPut,
	put:    containerMetadataTemplatesPostPut,
	delete: containerMetadataTemplatesDelete,
}

var containerBackupsCmd = Command{
	name: "containers/{name}/backups",
	get:  containerBackupsGet,
	post: containerBackupsPost,
}

var containerBackupCmd = Command{
	name:   "containers/{name}/backups/{backupName}",
	get:    containerBackupGet,
	post:   containerBackupPost,
	delete: containerBackupDelete,
}

var containerBackupExportCmd = Command{
	name: "containers/{name}/backups/{backupName}/export",
	get:  containerBackupExportGet,
}

type containerAutostartList []container

func (slice containerAutostartList) Len() int {
	return len(slice)
}

func (slice containerAutostartList) Less(i, j int) bool {
	iOrder := slice[i].ExpandedConfig()["boot.autostart.priority"]
	jOrder := slice[j].ExpandedConfig()["boot.autostart.priority"]

	if iOrder != jOrder {
		iOrderInt, _ := strconv.Atoi(iOrder)
		jOrderInt, _ := strconv.Atoi(jOrder)
		return iOrderInt > jOrderInt
	}

	return slice[i].Name() < slice[j].Name()
}

func (slice containerAutostartList) Swap(i, j int) {
	slice[i], slice[j] = slice[j], slice[i]
}

func containersRestart(s *state.State) error {
	// Get all the containers
	result, err := s.Cluster.ContainersNodeList(db.CTypeRegular)
	if err != nil {
		return err
	}

	containers := []container{}

	for _, name := range result {
		c, err := containerLoadByName(s, name)
		if err != nil {
			return err
		}

		containers = append(containers, c)
	}

	sort.Sort(containerAutostartList(containers))

	// Restart the containers
	for _, c := range containers {
		config := c.ExpandedConfig()
		lastState := config["volatile.last_state.power"]

		autoStart := config["boot.autostart"]
		autoStartDelay := config["boot.autostart.delay"]

		if shared.IsTrue(autoStart) || (autoStart == "" && lastState == "RUNNING") {
			if c.IsRunning() {
				continue
			}

			err = c.Start(false)
			if err != nil {
				logger.Errorf("Failed to start container '%s': %v", c.Name(), err)
			}

			autoStartDelayInt, err := strconv.Atoi(autoStartDelay)
			if err == nil {
				time.Sleep(time.Duration(autoStartDelayInt) * time.Second)
			}
		}
	}

	return nil
}

type containerStopList []container

func (slice containerStopList) Len() int {
	return len(slice)
}

func (slice containerStopList) Less(i, j int) bool {
	iOrder := slice[i].ExpandedConfig()["boot.stop.priority"]
	jOrder := slice[j].ExpandedConfig()["boot.stop.priority"]

	if iOrder != jOrder {
		iOrderInt, _ := strconv.Atoi(iOrder)
		jOrderInt, _ := strconv.Atoi(jOrder)
		return iOrderInt > jOrderInt // check this line (prob <)
	}

	return slice[i].Name() < slice[j].Name()
}

func (slice containerStopList) Swap(i, j int) {
	slice[i], slice[j] = slice[j], slice[i]
}

func containersShutdown(s *state.State) error {
	var wg sync.WaitGroup

	// Get all the containers
	results, err := s.Cluster.ContainersList(db.CTypeRegular)
	if err != nil {
		return err
	}

	containers := []container{}

	for _, name := range results {
		c, err := containerLoadByName(s, name)
		if err != nil {
			return err
		}

		containers = append(containers, c)
	}

	sort.Sort(containerStopList(containers))

	// Reset all container states
	err = s.Cluster.ContainersResetState()
	if err != nil {
		return err
	}

	var lastPriority int = 0

	if len(containers) != 0 {
		lastPriority, _ = strconv.Atoi(containers[0].ExpandedConfig()["boot.stop.priority"])
	}

	for _, c := range containers {
		priority, _ := strconv.Atoi(c.ExpandedConfig()["boot.stop.priority"])

		// Enforce shutdown priority
		if priority != lastPriority {
			lastPriority = priority

			// Wait for containers with higher priority to finish
			wg.Wait()
		}

		// Record the current state
		lastState := c.State()

		// Stop the container
		if lastState != "BROKEN" && lastState != "STOPPED" {
			// Determinate how long to wait for the container to shutdown cleanly
			var timeoutSeconds int
			value, ok := c.ExpandedConfig()["boot.host_shutdown_timeout"]
			if ok {
				timeoutSeconds, _ = strconv.Atoi(value)
			} else {
				timeoutSeconds = 30
			}

			// Stop the container
			wg.Add(1)
			go func(c container, lastState string) {
				c.Shutdown(time.Second * time.Duration(timeoutSeconds))
				c.Stop(false)
				c.ConfigKeySet("volatile.last_state.power", lastState)

				wg.Done()
			}(c, lastState)
		} else {
			c.ConfigKeySet("volatile.last_state.power", lastState)
		}
	}
	wg.Wait()

	return nil
}

func containerDeleteSnapshots(s *state.State, cname string) error {
	results, err := s.Cluster.ContainerGetSnapshots(cname)
	if err != nil {
		return err
	}

	for _, sname := range results {
		sc, err := containerLoadByName(s, sname)
		if err != nil {
			logger.Error(
				"containerDeleteSnapshots: Failed to load the snapshotcontainer",
				log.Ctx{"container": cname, "snapshot": sname})

			continue
		}

		if err := sc.Delete(); err != nil {
			logger.Error(
				"containerDeleteSnapshots: Failed to delete a snapshotcontainer",
				log.Ctx{"container": cname, "snapshot": sname, "err": err})
		}
	}

	return nil
}
<|endoftext|>


<commit_before>// Copyright 2022 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 slog

import (
	"bytes"
	"context"
	"io"
	"log"
	"path/filepath"
	"regexp"
	"runtime"
	"strings"
	"testing"
	"time"
)

const timeRE = `\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}(Z|[+-]\d{2}:\d{2})`

func TestLogTextHandler(t *testing.T) {
	var buf bytes.Buffer

	l := New(NewTextHandler(&buf))

	check := func(want string) {
		t.Helper()
		if want != "" {
			want = "time=" + timeRE + " " + want
		}
		checkLogOutput(t, buf.String(), want)
		buf.Reset()
	}

	l.Info("msg", "a", 1, "b", 2)
	check(`level=INFO msg=msg a=1 b=2`)

	// By default, debug messages are not printed.
	l.Debug("bg", Int("a", 1), "b", 2)
	check("")

	l.Warn("w", Duration("dur", 3*time.Second))
	check(`level=WARN msg=w dur=3s`)

	l.Error("bad", io.EOF, "a", 1)
	check(`level=ERROR msg=bad a=1 err=EOF`)

	l.Log(WarnLevel+1, "w", Int("a", 1), String("b", "two"))
	check(`level=WARN\+1 msg=w a=1 b=two`)

	l.LogAttrs(InfoLevel+1, "a b c", Int("a", 1), String("b", "two"))
	check(`level=INFO\+1 msg="a b c" a=1 b=two`)
}

func TestConnections(t *testing.T) {
	var logbuf, slogbuf bytes.Buffer

	// The default slog.Logger's handler uses the log package's default output.
	log.SetOutput(&logbuf)
	log.SetFlags(log.Flags() | log.Lshortfile)
	Info("msg", "a", 1)
	checkLogOutput(t, logbuf.String(),
		`\d{4}/\d{2}/\d{2} \d{2}:\d{2}:\d{2} logger_test.go:\d\d: INFO msg a=1`)
	logbuf.Reset()
	Warn("msg", "b", 2)
	checkLogOutput(t, logbuf.String(),
		`\d{4}/\d{2}/\d{2} \d{2}:\d{2}:\d{2} logger_test.go:\d\d: WARN msg b=2`)
	// Levels below Info are not printed.
	logbuf.Reset()
	Debug("msg", "c", 3)
	checkLogOutput(t, logbuf.String(), "")

	// Once slog.SetDefault is called, the direction is reversed: the default
	// log.Logger's output goes through the handler.
	SetDefault(New(NewTextHandler(&slogbuf)))
	log.Print("msg2")
	checkLogOutput(t, slogbuf.String(), "time="+timeRE+` level=INFO msg=msg2`)

	// Setting log's output again breaks the connection.
	logbuf.Reset()
	slogbuf.Reset()
	log.SetOutput(&logbuf)
	log.SetFlags(log.LstdFlags)
	log.Print("msg3")
	checkLogOutput(t, logbuf.String(),
		`\d{4}/\d{2}/\d{2} \d{2}:\d{2}:\d{2} msg3`)
	if got := slogbuf.String(); got != "" {
		t.Errorf("got %q, want empty", got)
	}
}

func TestAttrs(t *testing.T) {
	check := func(got []Attr, want ...Attr) {
		t.Helper()
		if !attrsEqual(got, want) {
			t.Errorf("got %v, want %v", got, want)
		}
	}

	l1 := New(&captureHandler{}).With("a", 1)
	l2 := New(l1.Handler()).With("b", 2)
	l2.Info("m", "c", 3)
	h := l2.Handler().(*captureHandler)
	check(h.attrs, Int("a", 1), Int("b", 2))
	check(attrsSlice(h.r), Int("c", 3))
}

func TestCallDepth(t *testing.T) {
	h := &captureHandler{}
	var startLine int

	check := func(count int) {
		t.Helper()
		const wantFile = "logger_test.go"
		wantLine := startLine + count*2
		gotFile, gotLine := h.r.SourceLine()
		gotFile = filepath.Base(gotFile)
		if gotFile != wantFile || gotLine != wantLine {
			t.Errorf("got (%s, %d), want (%s, %d)", gotFile, gotLine, wantFile, wantLine)
		}
	}

	logger := New(h)
	SetDefault(logger)

	// Calls to check must be one line apart.
	// Determine line where calls start.
	f, _ := runtime.CallersFrames([]uintptr{pc(2)}).Next()
	startLine = f.Line + 4
	// Do not change the number of lines between here and the call to check(0).

	logger.Log(InfoLevel, "")
	check(0)
	logger.LogAttrs(InfoLevel, "")
	check(1)
	logger.Debug("")
	check(2)
	logger.Info("")
	check(3)
	logger.Warn("")
	check(4)
	logger.Error("", nil)
	check(5)
	Debug("")
	check(6)
	Info("")
	check(7)
	Warn("")
	check(8)
	Error("", nil)
	check(9)
	Log(InfoLevel, "")
	check(10)
	LogAttrs(InfoLevel, "")
	check(11)
}

func TestAlloc(t *testing.T) {
	dl := New(discardHandler{})
	defer func(d Logger) { SetDefault(d) }(Default())
	SetDefault(dl)

	t.Run("Info", func(t *testing.T) {
		wantAllocs(t, 0, func() { Info("hello") })
	})
	// t.Run("Error", func(t *testing.T) {
	// 	wantAllocs(t, 0, func() { Error("hello", io.EOF) })
	// })
	t.Run("logger.Info", func(t *testing.T) {
		wantAllocs(t, 0, func() { dl.Info("hello") })
	})
	t.Run("logger.Log", func(t *testing.T) {
		wantAllocs(t, 0, func() { dl.Log(DebugLevel, "hello") })
	})
	t.Run("2 pairs", func(t *testing.T) {
		s := "abc"
		i := 2000
		wantAllocs(t, 2, func() {
			dl.Info("hello",
				"n", i,
				"s", s,
			)
		})
	})
	t.Run("2 pairs disabled inline", func(t *testing.T) {
		l := New(discardHandler{disabled: true})
		s := "abc"
		i := 2000
		wantAllocs(t, 2, func() {
			l.Log(InfoLevel, "hello",
				"n", i,
				"s", s,
			)
		})
	})
	t.Run("2 pairs disabled", func(t *testing.T) {
		l := New(discardHandler{disabled: true})
		s := "abc"
		i := 2000
		wantAllocs(t, 0, func() {
			if l.Enabled(InfoLevel) {
				l.Log(InfoLevel, "hello",
					"n", i,
					"s", s,
				)
			}
		})
	})
	t.Run("9 kvs", func(t *testing.T) {
		s := "abc"
		i := 2000
		d := time.Second
		wantAllocs(t, 11, func() {
			dl.Info("hello",
				"n", i, "s", s, "d", d,
				"n", i, "s", s, "d", d,
				"n", i, "s", s, "d", d)
		})
	})
	t.Run("pairs", func(t *testing.T) {
		wantAllocs(t, 0, func() { dl.Info("", "error", io.EOF) })
	})
	t.Run("attrs1", func(t *testing.T) {
		wantAllocs(t, 0, func() { dl.LogAttrs(InfoLevel, "", Int("a", 1)) })
		wantAllocs(t, 0, func() { dl.LogAttrs(InfoLevel, "", Any("error", io.EOF)) })
	})
	t.Run("attrs3", func(t *testing.T) {
		wantAllocs(t, 0, func() {
			dl.LogAttrs(InfoLevel, "hello", Int("a", 1), String("b", "two"), Duration("c", time.Second))
		})
	})
	t.Run("attrs3 disabled", func(t *testing.T) {
		logger := New(discardHandler{disabled: true})
		wantAllocs(t, 0, func() {
			logger.LogAttrs(InfoLevel, "hello", Int("a", 1), String("b", "two"), Duration("c", time.Second))
		})
	})
	t.Run("attrs6", func(t *testing.T) {
		wantAllocs(t, 1, func() {
			dl.LogAttrs(InfoLevel, "hello",
				Int("a", 1), String("b", "two"), Duration("c", time.Second),
				Int("d", 1), String("e", "two"), Duration("f", time.Second))
		})
	})
	t.Run("attrs9", func(t *testing.T) {
		wantAllocs(t, 1, func() {
			dl.LogAttrs(InfoLevel, "hello",
				Int("a", 1), String("b", "two"), Duration("c", time.Second),
				Int("d", 1), String("e", "two"), Duration("f", time.Second),
				Int("d", 1), String("e", "two"), Duration("f", time.Second))
		})
	})
}

func TestSetAttrs(t *testing.T) {
	for _, test := range []struct {
		args []any
		want []Attr
	}{
		{nil, nil},
		{[]any{"a", 1}, []Attr{Int("a", 1)}},
		{[]any{"a", 1, "b", "two"}, []Attr{Int("a", 1), String("b", "two")}},
		{[]any{"a"}, []Attr{String(badKey, "a")}},
		{[]any{"a", 1, "b"}, []Attr{Int("a", 1), String(badKey, "b")}},
		{[]any{"a", 1, 2, 3}, []Attr{Int("a", 1), Int(badKey, 2), Int(badKey, 3)}},
	} {
		r := NewRecord(time.Time{}, 0, "", 0, nil)
		r.setAttrsFromArgs(test.args)
		got := attrsSlice(r)
		if !attrsEqual(got, test.want) {
			t.Errorf("%v:\ngot  %v\nwant %v", test.args, got, test.want)
		}
	}
}

func checkLogOutput(t *testing.T, got, wantRegexp string) {
	t.Helper()
	got = clean(got)
	wantRegexp = "^" + wantRegexp + "$"
	matched, err := regexp.MatchString(wantRegexp, got)
	if err != nil {
		t.Fatal(err)
	}
	if !matched {
		t.Errorf("\ngot  %s\nwant %s", got, wantRegexp)
	}
}

// clean prepares log output for comparison.
func clean(s string) string {
	if len(s) > 0 && s[len(s)-1] == '\n' {
		s = s[:len(s)-1]
	}
	return strings.ReplaceAll(s, "\n", "~")
}

type captureHandler struct {
	r     Record
	attrs []Attr
}

func (h *captureHandler) Handle(r Record) error {
	h.r = r
	return nil
}

func (*captureHandler) Enabled(Level) bool { return true }

func (c *captureHandler) WithAttrs(as []Attr) Handler {
	c2 := *c
	c2.attrs = concat(c2.attrs, as)
	return &c2
}

func (h *captureHandler) WithGroup(name string) Handler {
	panic("unimplemented")
}

type discardHandler struct {
	disabled bool
	attrs    []Attr
}

func (d discardHandler) Enabled(Level) bool { return !d.disabled }
func (discardHandler) Handle(Record) error  { return nil }
func (d discardHandler) WithAttrs(as []Attr) Handler {
	d.attrs = concat(d.attrs, as)
	return d
}
func (h discardHandler) WithGroup(name string) Handler {
	return h
}

// This is a simple benchmark. See the benchmarks subdirectory for more extensive ones.
func BenchmarkNopLog(b *testing.B) {
	b.ReportAllocs()
	l := New(&captureHandler{})
	b.Run("attrs", func(b *testing.B) {
		for i := 0; i < b.N; i++ {
			l.LogAttrs(InfoLevel, "msg", Int("a", 1), String("b", "two"), Bool("c", true))
		}
	})
	b.Run("attrs-parallel", func(b *testing.B) {
		b.RunParallel(func(pb *testing.PB) {
			for pb.Next() {
				l.LogAttrs(InfoLevel, "msg", Int("a", 1), String("b", "two"), Bool("c", true))
			}
		})
	})
	b.Run("keys-values", func(b *testing.B) {
		for i := 0; i < b.N; i++ {
			l.Log(InfoLevel, "msg", "a", 1, "b", "two", "c", true)
		}
	})
}

func TestSetDefault(t *testing.T) {
	// Verify that setting the default to itself does not result in deadlock.
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	defer func(w io.Writer) { log.SetOutput(w) }(log.Writer())
	log.SetOutput(io.Discard)
	go func() {
		Info("A")
		SetDefault(Default())
		Info("B")
		cancel()
	}()
	<-ctx.Done()
	if err := ctx.Err(); err != context.Canceled {
		t.Errorf("wanted canceled, got %v", err)
	}
}

// concat returns a new slice with the elements of s1 followed
// by those of s2. The slice has no additional capacity.
func concat[T any](s1, s2 []T) []T {
	s := make([]T, len(s1)+len(s2))
	copy(s, s1)
	copy(s[len(s1):], s2)
	return s
}
<commit_msg>slog: reorder some test code<commit_after>// Copyright 2022 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 slog

import (
	"bytes"
	"context"
	"io"
	"log"
	"path/filepath"
	"regexp"
	"runtime"
	"strings"
	"testing"
	"time"
)

const timeRE = `\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}(Z|[+-]\d{2}:\d{2})`

func TestLogTextHandler(t *testing.T) {
	var buf bytes.Buffer

	l := New(NewTextHandler(&buf))

	check := func(want string) {
		t.Helper()
		if want != "" {
			want = "time=" + timeRE + " " + want
		}
		checkLogOutput(t, buf.String(), want)
		buf.Reset()
	}

	l.Info("msg", "a", 1, "b", 2)
	check(`level=INFO msg=msg a=1 b=2`)

	// By default, debug messages are not printed.
	l.Debug("bg", Int("a", 1), "b", 2)
	check("")

	l.Warn("w", Duration("dur", 3*time.Second))
	check(`level=WARN msg=w dur=3s`)

	l.Error("bad", io.EOF, "a", 1)
	check(`level=ERROR msg=bad a=1 err=EOF`)

	l.Log(WarnLevel+1, "w", Int("a", 1), String("b", "two"))
	check(`level=WARN\+1 msg=w a=1 b=two`)

	l.LogAttrs(InfoLevel+1, "a b c", Int("a", 1), String("b", "two"))
	check(`level=INFO\+1 msg="a b c" a=1 b=two`)
}

func TestConnections(t *testing.T) {
	var logbuf, slogbuf bytes.Buffer

	// The default slog.Logger's handler uses the log package's default output.
	log.SetOutput(&logbuf)
	log.SetFlags(log.Flags() | log.Lshortfile)
	Info("msg", "a", 1)
	checkLogOutput(t, logbuf.String(),
		`\d{4}/\d{2}/\d{2} \d{2}:\d{2}:\d{2} logger_test.go:\d\d: INFO msg a=1`)
	logbuf.Reset()
	Warn("msg", "b", 2)
	checkLogOutput(t, logbuf.String(),
		`\d{4}/\d{2}/\d{2} \d{2}:\d{2}:\d{2} logger_test.go:\d\d: WARN msg b=2`)
	// Levels below Info are not printed.
	logbuf.Reset()
	Debug("msg", "c", 3)
	checkLogOutput(t, logbuf.String(), "")

	// Once slog.SetDefault is called, the direction is reversed: the default
	// log.Logger's output goes through the handler.
	SetDefault(New(NewTextHandler(&slogbuf)))
	log.Print("msg2")
	checkLogOutput(t, slogbuf.String(), "time="+timeRE+` level=INFO msg=msg2`)

	// Setting log's output again breaks the connection.
	logbuf.Reset()
	slogbuf.Reset()
	log.SetOutput(&logbuf)
	log.SetFlags(log.LstdFlags)
	log.Print("msg3")
	checkLogOutput(t, logbuf.String(),
		`\d{4}/\d{2}/\d{2} \d{2}:\d{2}:\d{2} msg3`)
	if got := slogbuf.String(); got != "" {
		t.Errorf("got %q, want empty", got)
	}
}

func TestAttrs(t *testing.T) {
	check := func(got []Attr, want ...Attr) {
		t.Helper()
		if !attrsEqual(got, want) {
			t.Errorf("got %v, want %v", got, want)
		}
	}

	l1 := New(&captureHandler{}).With("a", 1)
	l2 := New(l1.Handler()).With("b", 2)
	l2.Info("m", "c", 3)
	h := l2.Handler().(*captureHandler)
	check(h.attrs, Int("a", 1), Int("b", 2))
	check(attrsSlice(h.r), Int("c", 3))
}

func TestCallDepth(t *testing.T) {
	h := &captureHandler{}
	var startLine int

	check := func(count int) {
		t.Helper()
		const wantFile = "logger_test.go"
		wantLine := startLine + count*2
		gotFile, gotLine := h.r.SourceLine()
		gotFile = filepath.Base(gotFile)
		if gotFile != wantFile || gotLine != wantLine {
			t.Errorf("got (%s, %d), want (%s, %d)", gotFile, gotLine, wantFile, wantLine)
		}
	}

	logger := New(h)
	SetDefault(logger)

	// Calls to check must be one line apart.
	// Determine line where calls start.
	f, _ := runtime.CallersFrames([]uintptr{pc(2)}).Next()
	startLine = f.Line + 4
	// Do not change the number of lines between here and the call to check(0).

	logger.Log(InfoLevel, "")
	check(0)
	logger.LogAttrs(InfoLevel, "")
	check(1)
	logger.Debug("")
	check(2)
	logger.Info("")
	check(3)
	logger.Warn("")
	check(4)
	logger.Error("", nil)
	check(5)
	Debug("")
	check(6)
	Info("")
	check(7)
	Warn("")
	check(8)
	Error("", nil)
	check(9)
	Log(InfoLevel, "")
	check(10)
	LogAttrs(InfoLevel, "")
	check(11)
}

func TestAlloc(t *testing.T) {
	dl := New(discardHandler{})
	defer func(d Logger) { SetDefault(d) }(Default())
	SetDefault(dl)

	t.Run("Info", func(t *testing.T) {
		wantAllocs(t, 0, func() { Info("hello") })
	})
	// t.Run("Error", func(t *testing.T) {
	// 	wantAllocs(t, 0, func() { Error("hello", io.EOF) })
	// })
	t.Run("logger.Info", func(t *testing.T) {
		wantAllocs(t, 0, func() { dl.Info("hello") })
	})
	t.Run("logger.Log", func(t *testing.T) {
		wantAllocs(t, 0, func() { dl.Log(DebugLevel, "hello") })
	})
	t.Run("2 pairs", func(t *testing.T) {
		s := "abc"
		i := 2000
		wantAllocs(t, 2, func() {
			dl.Info("hello",
				"n", i,
				"s", s,
			)
		})
	})
	t.Run("2 pairs disabled inline", func(t *testing.T) {
		l := New(discardHandler{disabled: true})
		s := "abc"
		i := 2000
		wantAllocs(t, 2, func() {
			l.Log(InfoLevel, "hello",
				"n", i,
				"s", s,
			)
		})
	})
	t.Run("2 pairs disabled", func(t *testing.T) {
		l := New(discardHandler{disabled: true})
		s := "abc"
		i := 2000
		wantAllocs(t, 0, func() {
			if l.Enabled(InfoLevel) {
				l.Log(InfoLevel, "hello",
					"n", i,
					"s", s,
				)
			}
		})
	})
	t.Run("9 kvs", func(t *testing.T) {
		s := "abc"
		i := 2000
		d := time.Second
		wantAllocs(t, 11, func() {
			dl.Info("hello",
				"n", i, "s", s, "d", d,
				"n", i, "s", s, "d", d,
				"n", i, "s", s, "d", d)
		})
	})
	t.Run("pairs", func(t *testing.T) {
		wantAllocs(t, 0, func() { dl.Info("", "error", io.EOF) })
	})
	t.Run("attrs1", func(t *testing.T) {
		wantAllocs(t, 0, func() { dl.LogAttrs(InfoLevel, "", Int("a", 1)) })
		wantAllocs(t, 0, func() { dl.LogAttrs(InfoLevel, "", Any("error", io.EOF)) })
	})
	t.Run("attrs3", func(t *testing.T) {
		wantAllocs(t, 0, func() {
			dl.LogAttrs(InfoLevel, "hello", Int("a", 1), String("b", "two"), Duration("c", time.Second))
		})
	})
	t.Run("attrs3 disabled", func(t *testing.T) {
		logger := New(discardHandler{disabled: true})
		wantAllocs(t, 0, func() {
			logger.LogAttrs(InfoLevel, "hello", Int("a", 1), String("b", "two"), Duration("c", time.Second))
		})
	})
	t.Run("attrs6", func(t *testing.T) {
		wantAllocs(t, 1, func() {
			dl.LogAttrs(InfoLevel, "hello",
				Int("a", 1), String("b", "two"), Duration("c", time.Second),
				Int("d", 1), String("e", "two"), Duration("f", time.Second))
		})
	})
	t.Run("attrs9", func(t *testing.T) {
		wantAllocs(t, 1, func() {
			dl.LogAttrs(InfoLevel, "hello",
				Int("a", 1), String("b", "two"), Duration("c", time.Second),
				Int("d", 1), String("e", "two"), Duration("f", time.Second),
				Int("d", 1), String("e", "two"), Duration("f", time.Second))
		})
	})
}

func TestSetAttrs(t *testing.T) {
	for _, test := range []struct {
		args []any
		want []Attr
	}{
		{nil, nil},
		{[]any{"a", 1}, []Attr{Int("a", 1)}},
		{[]any{"a", 1, "b", "two"}, []Attr{Int("a", 1), String("b", "two")}},
		{[]any{"a"}, []Attr{String(badKey, "a")}},
		{[]any{"a", 1, "b"}, []Attr{Int("a", 1), String(badKey, "b")}},
		{[]any{"a", 1, 2, 3}, []Attr{Int("a", 1), Int(badKey, 2), Int(badKey, 3)}},
	} {
		r := NewRecord(time.Time{}, 0, "", 0, nil)
		r.setAttrsFromArgs(test.args)
		got := attrsSlice(r)
		if !attrsEqual(got, test.want) {
			t.Errorf("%v:\ngot  %v\nwant %v", test.args, got, test.want)
		}
	}
}

func TestSetDefault(t *testing.T) {
	// Verify that setting the default to itself does not result in deadlock.
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	defer func(w io.Writer) { log.SetOutput(w) }(log.Writer())
	log.SetOutput(io.Discard)
	go func() {
		Info("A")
		SetDefault(Default())
		Info("B")
		cancel()
	}()
	<-ctx.Done()
	if err := ctx.Err(); err != context.Canceled {
		t.Errorf("wanted canceled, got %v", err)
	}
}

func checkLogOutput(t *testing.T, got, wantRegexp string) {
	t.Helper()
	got = clean(got)
	wantRegexp = "^" + wantRegexp + "$"
	matched, err := regexp.MatchString(wantRegexp, got)
	if err != nil {
		t.Fatal(err)
	}
	if !matched {
		t.Errorf("\ngot  %s\nwant %s", got, wantRegexp)
	}
}

// clean prepares log output for comparison.
func clean(s string) string {
	if len(s) > 0 && s[len(s)-1] == '\n' {
		s = s[:len(s)-1]
	}
	return strings.ReplaceAll(s, "\n", "~")
}

type captureHandler struct {
	r     Record
	attrs []Attr
}

func (h *captureHandler) Handle(r Record) error {
	h.r = r
	return nil
}

func (*captureHandler) Enabled(Level) bool { return true }

func (c *captureHandler) WithAttrs(as []Attr) Handler {
	c2 := *c
	c2.attrs = concat(c2.attrs, as)
	return &c2
}

func (h *captureHandler) WithGroup(name string) Handler {
	panic("unimplemented")
}

type discardHandler struct {
	disabled bool
	attrs    []Attr
}

func (d discardHandler) Enabled(Level) bool { return !d.disabled }
func (discardHandler) Handle(Record) error  { return nil }
func (d discardHandler) WithAttrs(as []Attr) Handler {
	d.attrs = concat(d.attrs, as)
	return d
}
func (h discardHandler) WithGroup(name string) Handler {
	return h
}

// concat returns a new slice with the elements of s1 followed
// by those of s2. The slice has no additional capacity.
func concat[T any](s1, s2 []T) []T {
	s := make([]T, len(s1)+len(s2))
	copy(s, s1)
	copy(s[len(s1):], s2)
	return s
}

// This is a simple benchmark. See the benchmarks subdirectory for more extensive ones.
func BenchmarkNopLog(b *testing.B) {
	b.ReportAllocs()
	l := New(&captureHandler{})
	b.Run("attrs", func(b *testing.B) {
		for i := 0; i < b.N; i++ {
			l.LogAttrs(InfoLevel, "msg", Int("a", 1), String("b", "two"), Bool("c", true))
		}
	})
	b.Run("attrs-parallel", func(b *testing.B) {
		b.RunParallel(func(pb *testing.PB) {
			for pb.Next() {
				l.LogAttrs(InfoLevel, "msg", Int("a", 1), String("b", "two"), Bool("c", true))
			}
		})
	})
	b.Run("keys-values", func(b *testing.B) {
		for i := 0; i < b.N; i++ {
			l.Log(InfoLevel, "msg", "a", 1, "b", "two", "c", true)
		}
	})
}
<|endoftext|>


<commit_before>/*

Package vm implements the virtual machine used to run the bytecode
generated by http://github.com/lestrrat/go-xslate/compiler

The virtual machine is an extremely simple one: each opcode in the
bytecode sequence returns the next opcode to execute. The virtual machine
just keeps on calling the opcode until we reach the "end" opcode.

The virtual machine accepts the bytecode, and input variables:

  vm.Run(bytecode, variables)

*/
package vm

import (
	"bufio"
	"fmt"
	"io"

	"github.com/lestrrat/go-xslate/internal/rvpool"
)

// NewVM creates a new VM
func NewVM() *VM {
	return &VM{NewState(), nil, nil}
}

func (vm *VM) SetFunctions(vars Vars) {
	vm.functions = vars
}

// CurrentOp returns the current Op to be executed
func (vm *VM) CurrentOp() Op {
	return vm.st.CurrentOp()
}

// IsSupportedByteCodeVersion returns true if this VM can handle the
// provided bytecode version
func (vm *VM) IsSupportedByteCodeVersion(bc *ByteCode) bool {
	return bc.Version == 1.0
}

// Run executes the given vm.ByteCode using the given variables. For historical
// reasons, it also allows re-executing the previous bytecode instructions
// given to a virtual machine, but this will probably be removed in the future
func (vm *VM) Run(bc *ByteCode, vars Vars, output io.Writer) {
	if !vm.IsSupportedByteCodeVersion(bc) {
		panic(fmt.Sprintf(
			"error: ByteCode version %f no supported",
			bc.Version,
		))
	}

	st := vm.st

	if _, ok := output.(*bufio.Writer); !ok {
		output = bufio.NewWriter(output)
		defer output.(*bufio.Writer).Flush()
	}
	st.Reset()
	st.pc = bc
	st.output = output
	newvars := Vars(rvpool.Get())
	defer rvpool.Release(newvars)
	defer newvars.Reset()

	st.vars = newvars
	if fc := vm.functions; fc != nil {
		for k, v := range vm.functions {
			st.vars[k] = v
		}
	}

	if vars != nil {
		for k, v := range vars {
			st.vars[k] = v
		}
	}
	st.Loader = vm.Loader

	// This is the main loop
	for op := st.CurrentOp(); op.Type() != TXOPEnd; op = st.CurrentOp() {
		op.Call(st)
	}
}
<commit_msg>tweak style<commit_after>/*

Package vm implements the virtual machine used to run the bytecode
generated by http://github.com/lestrrat/go-xslate/compiler

The virtual machine is an extremely simple one: each opcode in the
bytecode sequence returns the next opcode to execute. The virtual machine
just keeps on calling the opcode until we reach the "end" opcode.

The virtual machine accepts the bytecode, and input variables:

  vm.Run(bytecode, variables)

*/
package vm

import (
	"bufio"
	"fmt"
	"io"

	"github.com/lestrrat/go-xslate/internal/rvpool"
)

// NewVM creates a new VM
func NewVM() *VM {
	return &VM{
		st:        NewState(),
		functions: nil,
		Loader:    nil,
	}
}

func (vm *VM) SetFunctions(vars Vars) {
	vm.functions = vars
}

// CurrentOp returns the current Op to be executed
func (vm *VM) CurrentOp() Op {
	return vm.st.CurrentOp()
}

// IsSupportedByteCodeVersion returns true if this VM can handle the
// provided bytecode version
func (vm *VM) IsSupportedByteCodeVersion(bc *ByteCode) bool {
	return bc.Version == 1.0
}

// Run executes the given vm.ByteCode using the given variables. For historical
// reasons, it also allows re-executing the previous bytecode instructions
// given to a virtual machine, but this will probably be removed in the future
func (vm *VM) Run(bc *ByteCode, vars Vars, output io.Writer) {
	if !vm.IsSupportedByteCodeVersion(bc) {
		panic(fmt.Sprintf(
			"error: ByteCode version %f no supported",
			bc.Version,
		))
	}

	st := vm.st

	if _, ok := output.(*bufio.Writer); !ok {
		output = bufio.NewWriter(output)
		defer output.(*bufio.Writer).Flush()
	}
	st.Reset()
	st.pc = bc
	st.output = output
	newvars := Vars(rvpool.Get())
	defer rvpool.Release(newvars)
	defer newvars.Reset()

	st.vars = newvars
	if fc := vm.functions; fc != nil {
		for k, v := range vm.functions {
			st.vars[k] = v
		}
	}

	if vars != nil {
		for k, v := range vars {
			st.vars[k] = v
		}
	}
	st.Loader = vm.Loader

	// This is the main loop
	for op := st.CurrentOp(); op.Type() != TXOPEnd; op = st.CurrentOp() {
		op.Call(st)
	}
}
<|endoftext|>


<commit_before>package dendrite

import (
	"crypto/sha1"
	"encoding/binary"
	"fmt"
	"time"
)

// Any vnode (local or remote)
type Vnode struct {
	Id   []byte
	Host string // ip:port
}

func (vn *Vnode) String() string {
	return fmt.Sprintf("%x", vn.Id)
}

// local Vnode
type localVnode struct {
	Vnode
	ring        *Ring
	successors  []*Vnode // "backlog" of known successors
	finger      []*Vnode
	last_finger int
	predecessor *Vnode
	stabilized  time.Time
	timer       *time.Timer
}

func (vn *localVnode) init(idx int) {
	// combine hostname with idx to generate hash
	hash := sha1.New()
	hash.Write([]byte(vn.ring.config.Hostname))
	binary.Write(hash, binary.BigEndian, uint16(idx))
	vn.Id = hash.Sum(nil)
	vn.Host = vn.ring.config.Hostname
	vn.successors = make([]*Vnode, vn.ring.config.NumSuccessors)
	vn.finger = make([]*Vnode, 160) // keyspace size is 160 with SHA1
	vn.ring.transport.Register(&vn.Vnode, vn)
}

// Schedules the Vnode to do regular maintenence
func (vn *localVnode) schedule() {
	// Setup our stabilize timer
	vn.timer = time.AfterFunc(randStabilize(vn.ring.config), vn.stabilize)
}

func (vn *localVnode) stabilize() {
	defer vn.schedule()

}

// returns successor for requested id
// second return argument indicates whether client should forward request to another node
func (vn *localVnode) find_successor(id []byte) (*Vnode, bool) {
	// check if Id falls between me and my successor
	if between(vn.Id, vn.successors[0].Id, id, true) {
		return vn.successors[0], true
	}
	return vn.closest_preceeding_finger(id), false
}

// Find closest preceeding finger node
func (vn *localVnode) closest_preceeding_finger(id []byte) *Vnode {
	// keysize(i) down to 1
	for i := len(vn.finger) - 1; i >= 0; i-- {
		if vn.finger[i] == nil {
			continue
		}
		// check if id falls after this finger (finger[i] IN (n, id))
		if between(vn.Id, id, vn.finger[i].Id, false) {
			return vn.finger[i]
		}
	}
	return &vn.Vnode
}

// Notifies our successor of us, updates successor list
func (vn *localVnode) notifySuccessor() error {
	// Notify successor
	succ := vn.successors[0]
	succ_list, err := vn.ring.transport.Notify(succ, &vn.Vnode)
	if err != nil {
		return err
	}

	// Trim the successors list if too long
	max_succ := vn.ring.config.NumSuccessors
	if len(succ_list) > max_succ-1 {
		succ_list = succ_list[:max_succ-1]
	}

	// Update local successors list
	for idx, s := range succ_list {
		if s == nil {
			break
		}
		// Ensure we don't set ourselves as a successor!
		if s == nil || s.String() == vn.String() {
			break
		}
		vn.successors[idx+1] = s
	}
	return nil
}
<commit_msg>adds notify methods<commit_after>package dendrite

import (
	"crypto/sha1"
	"encoding/binary"
	"fmt"
	"time"
)

// Any vnode (local or remote)
type Vnode struct {
	Id   []byte
	Host string // ip:port
}

func (vn *Vnode) String() string {
	return fmt.Sprintf("%x", vn.Id)
}

// local Vnode
type localVnode struct {
	Vnode
	ring        *Ring
	successors  []*Vnode // "backlog" of known successors
	finger      []*Vnode
	last_finger int
	predecessor *Vnode
	stabilized  time.Time
	timer       *time.Timer
}

func (vn *localVnode) init(idx int) {
	// combine hostname with idx to generate hash
	hash := sha1.New()
	hash.Write([]byte(vn.ring.config.Hostname))
	binary.Write(hash, binary.BigEndian, uint16(idx))
	vn.Id = hash.Sum(nil)
	vn.Host = vn.ring.config.Hostname
	vn.successors = make([]*Vnode, vn.ring.config.NumSuccessors)
	vn.finger = make([]*Vnode, 160) // keyspace size is 160 with SHA1
	vn.ring.transport.Register(&vn.Vnode, vn)
}

// Schedules the Vnode to do regular maintenence
func (vn *localVnode) schedule() {
	// Setup our stabilize timer
	vn.timer = time.AfterFunc(randStabilize(vn.ring.config), vn.stabilize)
}

func (vn *localVnode) stabilize() {
	defer vn.schedule()
	// Notify the successor
	if err := vn.notifySuccessor(); err != nil {
		log.Printf("[stabilize] Error notifying successor: %s", err)
	}

}

// returns successor for requested id
// second return argument indicates whether client should forward request to another node
func (vn *localVnode) find_successor(id []byte) (*Vnode, bool) {
	// check if Id falls between me and my successor
	if between(vn.Id, vn.successors[0].Id, id, true) {
		return vn.successors[0], true
	}
	return vn.closest_preceeding_finger(id), false
}

// Find closest preceeding finger node
func (vn *localVnode) closest_preceeding_finger(id []byte) *Vnode {
	// keysize(i) down to 1
	for i := len(vn.finger) - 1; i >= 0; i-- {
		if vn.finger[i] == nil {
			continue
		}
		// check if id falls after this finger (finger[i] IN (n, id))
		if between(vn.Id, id, vn.finger[i].Id, false) {
			return vn.finger[i]
		}
	}
	return &vn.Vnode
}

// Notifies our successor of us, updates successor list
func (vn *localVnode) notifySuccessor() error {
	// Notify successor
	succ := vn.successors[0]
	succ_list, err := vn.ring.transport.Notify(succ, &vn.Vnode)
	if err != nil {
		return err
	}

	// Trim the successors list if too long
	max_succ := vn.ring.config.NumSuccessors
	if len(succ_list) > max_succ-1 {
		succ_list = succ_list[:max_succ-1]
	}

	// Update local successors list
	for idx, s := range succ_list {
		if s == nil {
			break
		}
		// Ensure we don't set ourselves as a successor!
		if s == nil || s.String() == vn.String() {
			break
		}
		vn.successors[idx+1] = s
	}
	return nil
}
<|endoftext|>


<commit_before>package standard

import (
	G "github.com/ionous/sashimi/game"
	. "github.com/ionous/sashimi/script"
)

func init() {
	// (note: the action uses props, so that stories can make any prop behave similar to an opener. )
	AddScript(func(s *Script) {
		s.The("props",
			Called("openers"),
			AreEither("open").Or("closed"),
			// note: unopenable sounds like it cant become open, rather than it cannot be opened (by someone).
			AreEither("openable").Or("not openable"),
			AreEither("locakable").Or("not lockable").Usually("not lockable"),
			AreEither("unlocked").Or("locked"),
		)

		//
		// Open:
		//
		s.The("actors",
			Can("open it").And("opening it").RequiresOne("prop"),
			To("open it", ReflectToTarget("report open")),
		)

		// "[regarding the noun][They] [aren't] something [we] [can] open."
		s.The("props",
			Can("report open").And("reporting open").RequiresOne("actor"),
			To("report open", func(g G.Play) {
				prop, actor := g.The("prop"), g.The("actor")
				if !prop.Is("openable") {
					prop.Go("report not openable", actor)
				} else {
					if prop.Is("open") {
						prop.Go("report already open", actor)
					} else {
						prop.IsNow("open")
						prop.Go("report now open", actor)
					}
				}
			}),
			Can("report not openable").And("reporting not openable").RequiresOne("actor"),
			To("report not openable", func(g G.Play) {
				g.Say("That's not something you can open.")
			}),
			Can("report already open").And("reporting already opened").RequiresOne("actor"),
			To("report already open", func(g G.Play) {
				g.Say("It's already opened.")
			}),
			Can("report now open").And("reporting now open").RequiresOne("actor"),
			To("report now open", func(g G.Play) {
				opener := g.The("opener")
				g.Say("Now the", opener.Text("Name"), "is open.")
				// if the noun doesnt not enclose the actor
				// list the contents of the noun, as a sentence, tersely, not listing concealed items;
				if opener.Is("opaque") {
					listContents(g, "In the", opener)
				}
			}),
		)

		//
		// Close:
		//
		// one visible thing, and requiring light
		s.The("actors",
			Can("close it").And("closing it").RequiresOne("prop"),
			To("close it", ReflectToTarget("report close")),
		)
		s.The("props",
			Can("report close").And("report closing").RequiresOne("actor"),
			To("report close", func(g G.Play) {
				prop, actor := g.The("prop"), g.The("actor")
				if !prop.Is("openable") {
					prop.Go("report not closeable", actor)
				} else {
					// FIX: locked?
					if prop.Is("closed") {
						prop.Go("report already closed", actor)
					} else {
						prop.IsNow("closed")
						prop.Go("report now closed", actor)
					}
				}
			}),
			Can("report not closeable").And("reporting not closeable").RequiresOne("actor"),
			To("report not closeable", func(g G.Play) {
				g.Say("That's not something you can close.")
			}),
			Can("report already closed").And("reporting already closed").RequiresOne("actor"),
			To("report already closed", func(g G.Play) {
				g.Say("It's already closed.") //[regarding the noun]?
			}),
			Can("report now closed").And("reporting now closed").RequiresOne("actor"),
			To("report now closed", func(g G.Play) {
				prop := g.The("prop")
				g.Say("Now the", prop.Text("Name"), "is closed.")
			}),
		)

		// understandings:
		s.Execute("open it", Matching("open {{something}}"))
		s.Execute("close it", Matching("close {{something}}"))
	})
}
<commit_msg>add fixme comment<commit_after>package standard

import (
	G "github.com/ionous/sashimi/game"
	. "github.com/ionous/sashimi/script"
)

func init() {
	// (note: the action uses props, so that stories can make any prop behave similar to an opener. )
	AddScript(func(s *Script) {
		s.The("props",
			Called("openers"),
			AreEither("open").Or("closed"),
			// note: unopenable sounds like it cant become open, rather than it cannot be opened (by someone).
			AreEither("openable").Or("not openable"),
			AreEither("locakable").Or("not lockable").Usually("not lockable"),
			AreEither("unlocked").Or("locked"),
		)

		//
		// Open:
		//
		s.The("actors",
			Can("open it").And("opening it").RequiresOne("prop"),
			To("open it", ReflectToTarget("report open")),
		)

		// "[regarding the noun][They] [aren't] something [we] [can] open."
		s.The("props",
			Can("report open").And("reporting open").RequiresOne("actor"),
			To("report open", func(g G.Play) {
				prop, actor := g.The("prop"), g.The("actor")
				if !prop.Is("openable") {
					prop.Go("report not openable", actor)
				} else {
					if prop.Is("open") {
						prop.Go("report already open", actor)
					} else {
						prop.IsNow("open")
						prop.Go("report now open", actor)
					}
				}
			}),
			Can("report not openable").And("reporting not openable").RequiresOne("actor"),
			To("report not openable", func(g G.Play) {
				g.Say("That's not something you can open.")
			}),
			Can("report already open").And("reporting already opened").RequiresOne("actor"),
			To("report already open", func(g G.Play) {
				g.Say("It's already opened.")
			}),
			Can("report now open").And("reporting now open").RequiresOne("actor"),
			To("report now open", func(g G.Play) {
				opener := g.The("opener")
				g.Say("Now the", opener.Text("Name"), "is open.")
				// if the noun doesnt not enclose the actor
				// list the contents of the noun, as a sentence, tersely, not listing concealed items;
				// FIX? not all openers are opaque/transparent, and not all openers have contents.
				if opener.Is("opaque") {
					listContents(g, "In the", opener)
				}
			}),
		)

		//
		// Close:
		//
		// one visible thing, and requiring light
		s.The("actors",
			Can("close it").And("closing it").RequiresOne("prop"),
			To("close it", ReflectToTarget("report close")),
		)
		s.The("props",
			Can("report close").And("report closing").RequiresOne("actor"),
			To("report close", func(g G.Play) {
				prop, actor := g.The("prop"), g.The("actor")
				if !prop.Is("openable") {
					prop.Go("report not closeable", actor)
				} else {
					// FIX: locked?
					if prop.Is("closed") {
						prop.Go("report already closed", actor)
					} else {
						prop.IsNow("closed")
						prop.Go("report now closed", actor)
					}
				}
			}),
			Can("report not closeable").And("reporting not closeable").RequiresOne("actor"),
			To("report not closeable", func(g G.Play) {
				g.Say("That's not something you can close.")
			}),
			Can("report already closed").And("reporting already closed").RequiresOne("actor"),
			To("report already closed", func(g G.Play) {
				g.Say("It's already closed.") //[regarding the noun]?
			}),
			Can("report now closed").And("reporting now closed").RequiresOne("actor"),
			To("report now closed", func(g G.Play) {
				prop := g.The("prop")
				g.Say("Now the", prop.Text("Name"), "is closed.")
			}),
		)

		// understandings:
		s.Execute("open it", Matching("open {{something}}"))
		s.Execute("close it", Matching("close {{something}}"))
	})
}
<|endoftext|>


<commit_before>package transloadit

import (
	"errors"
	"fmt"
	"gopkg.in/fsnotify.v1"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path"
	"strings"
	"time"
)

type WatchOptions struct {
	Input      string
	Output     string
	Watch      bool
	TemplateId string
	NotifyUrl  string
	Steps      map[string]map[string]interface{}
	Preserve   bool
}

type Watcher struct {
	client       *Client
	options      *WatchOptions
	stopped      bool
	Error        chan error
	Done         chan *AssemblyInfo
	Change       chan string
	end          chan bool
	recentWrites map[string]time.Time
	blacklist    map[string]bool
}

func (client *Client) Watch(options *WatchOptions) *Watcher {

	watcher := &Watcher{
		client:       client,
		options:      options,
		Error:        make(chan error),
		Done:         make(chan *AssemblyInfo),
		Change:       make(chan string),
		end:          make(chan bool),
		recentWrites: make(map[string]time.Time),
		blacklist:    make(map[string]bool),
	}

	watcher.start()

	return watcher

}

func (watcher *Watcher) start() {

	watcher.processDir()

	if watcher.options.Watch {
		go watcher.startWatcher()
	}

}

func (watcher *Watcher) Stop() {

	if watcher.stopped {
		return
	}

	watcher.stopped = true

	watcher.end <- true
	close(watcher.Done)
	close(watcher.Error)
	close(watcher.Change)
	close(watcher.end)
}

func (watcher *Watcher) processDir() {

	files, err := ioutil.ReadDir(watcher.options.Input)
	if err != nil {
		watcher.error(err)
		return
	}

	input := watcher.options.Input

	for _, file := range files {
		if !file.IsDir() {
			go watcher.processFile(path.Join(input, file.Name()))
		}
	}

}

func (watcher *Watcher) processFile(name string) {

	file, err := os.Open(name)
	if err != nil {
		watcher.error(err)
		return
	}

	// Add file to blacklist
	log.Printf("Adding to blacklist: '%s'", name)
	watcher.blacklist[name] = true

	assembly := watcher.client.CreateAssembly()

	if watcher.options.TemplateId != "" {
		assembly.TemplateId = watcher.options.TemplateId
	}

	if watcher.options.NotifyUrl != "" {
		assembly.NotifyUrl = watcher.options.NotifyUrl
	}

	for name, step := range watcher.options.Steps {
		assembly.AddStep(name, step)
	}

	assembly.Blocking = true

	assembly.AddReader("file", path.Base(name), file)

	info, err := assembly.Upload()
	if err != nil {
		watcher.error(err)
		return
	}

	if info.Error != "" {
		watcher.error(errors.New(info.Error))
		return
	}

	for stepName, results := range info.Results {
		for index, result := range results {
			go func() {
				watcher.downloadResult(stepName, index, result)
				watcher.handleOriginalFile(name)
				log.Printf("Removing from blacklist: '%s'", name)
				delete(watcher.blacklist, name)
				watcher.Done <- info
			}()
		}
	}
}

func (watcher *Watcher) downloadResult(stepName string, index int, result *FileInfo) {

	fileName := fmt.Sprintf("%s_%d_%s", stepName, index, result.Name)

	resp, err := http.Get(result.Url)
	if err != nil {
		watcher.error(err)
		return
	}

	defer resp.Body.Close()

	out, err := os.Create(path.Join(watcher.options.Output, fileName))
	if err != nil {
		watcher.error(err)
		return
	}

	defer out.Close()

	io.Copy(out, resp.Body)

}

func (watcher *Watcher) startWatcher() {

	fsWatcher, err := fsnotify.NewWatcher()
	if err != nil {
		watcher.error(err)
	}

	defer fsWatcher.Close()

	if err = fsWatcher.Add(watcher.options.Input); err != nil {
		watcher.error(err)
	}

	go func() {
		for {

			if watcher.stopped {
				return
			}

			time.Sleep(time.Second)
			now := time.Now()

			for name, lastEvent := range watcher.recentWrites {
				diff := now.Sub(lastEvent)
				if diff > (time.Millisecond * 500) {
					delete(watcher.recentWrites, name)
					watcher.Change <- name
					watcher.processFile(name)
				}
			}

		}
	}()

	for {
		select {
		case <-watcher.end:
			return
		case err := <-fsWatcher.Errors:
			watcher.error(err)
		case evt := <-fsWatcher.Events:
			// Ignore the event if the file is currently processed
			log.Printf("Checking blacklist: '%s'", evt.Name)
			if _, ok := watcher.blacklist[evt.Name]; ok == true {
				return
			}
			if evt.Op&fsnotify.Create == fsnotify.Create || evt.Op&fsnotify.Write == fsnotify.Write {
				watcher.recentWrites[evt.Name] = time.Now()
			}
		}
	}

}

func (watcher *Watcher) handleOriginalFile(name string) {

	var err error
	if watcher.options.Preserve {
		_, file := path.Split(name)
		err = os.Rename(name, watcher.options.Output+"/-original_0_"+basename(file))
	} else {
		err = os.Remove(name)
	}

	if err != nil {
		watcher.error(err)
	}

}

func (watcher *Watcher) error(err error) {
	watcher.Error <- err
}

func basename(name string) string {
	i := strings.LastIndex(name, string(os.PathSeparator))
	return name[i+1:]
}
<commit_msg>Process file in goroutine<commit_after>package transloadit

import (
	"errors"
	"fmt"
	"gopkg.in/fsnotify.v1"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path"
	"strings"
	"time"
)

type WatchOptions struct {
	Input      string
	Output     string
	Watch      bool
	TemplateId string
	NotifyUrl  string
	Steps      map[string]map[string]interface{}
	Preserve   bool
}

type Watcher struct {
	client       *Client
	options      *WatchOptions
	stopped      bool
	Error        chan error
	Done         chan *AssemblyInfo
	Change       chan string
	end          chan bool
	recentWrites map[string]time.Time
	blacklist    map[string]bool
}

func (client *Client) Watch(options *WatchOptions) *Watcher {

	watcher := &Watcher{
		client:       client,
		options:      options,
		Error:        make(chan error),
		Done:         make(chan *AssemblyInfo),
		Change:       make(chan string),
		end:          make(chan bool),
		recentWrites: make(map[string]time.Time),
		blacklist:    make(map[string]bool),
	}

	watcher.start()

	return watcher

}

func (watcher *Watcher) start() {

	watcher.processDir()

	if watcher.options.Watch {
		go watcher.startWatcher()
	}

}

func (watcher *Watcher) Stop() {

	if watcher.stopped {
		return
	}

	watcher.stopped = true

	watcher.end <- true
	close(watcher.Done)
	close(watcher.Error)
	close(watcher.Change)
	close(watcher.end)
}

func (watcher *Watcher) processDir() {

	files, err := ioutil.ReadDir(watcher.options.Input)
	if err != nil {
		watcher.error(err)
		return
	}

	input := watcher.options.Input

	for _, file := range files {
		if !file.IsDir() {
			go watcher.processFile(path.Join(input, file.Name()))
		}
	}

}

func (watcher *Watcher) processFile(name string) {

	file, err := os.Open(name)
	if err != nil {
		watcher.error(err)
		return
	}

	// Add file to blacklist
	log.Printf("Adding to blacklist: '%s'", name)
	watcher.blacklist[name] = true

	assembly := watcher.client.CreateAssembly()

	if watcher.options.TemplateId != "" {
		assembly.TemplateId = watcher.options.TemplateId
	}

	if watcher.options.NotifyUrl != "" {
		assembly.NotifyUrl = watcher.options.NotifyUrl
	}

	for name, step := range watcher.options.Steps {
		assembly.AddStep(name, step)
	}

	assembly.Blocking = true

	assembly.AddReader("file", path.Base(name), file)

	info, err := assembly.Upload()
	if err != nil {
		watcher.error(err)
		return
	}

	if info.Error != "" {
		watcher.error(errors.New(info.Error))
		return
	}

	for stepName, results := range info.Results {
		for index, result := range results {
			go func() {
				watcher.downloadResult(stepName, index, result)
				watcher.handleOriginalFile(name)
				log.Printf("Removing from blacklist: '%s'", name)
				delete(watcher.blacklist, name)
				watcher.Done <- info
			}()
		}
	}
}

func (watcher *Watcher) downloadResult(stepName string, index int, result *FileInfo) {

	fileName := fmt.Sprintf("%s_%d_%s", stepName, index, result.Name)

	resp, err := http.Get(result.Url)
	if err != nil {
		watcher.error(err)
		return
	}

	defer resp.Body.Close()

	out, err := os.Create(path.Join(watcher.options.Output, fileName))
	if err != nil {
		watcher.error(err)
		return
	}

	defer out.Close()

	io.Copy(out, resp.Body)

}

func (watcher *Watcher) startWatcher() {

	fsWatcher, err := fsnotify.NewWatcher()
	if err != nil {
		watcher.error(err)
	}

	defer fsWatcher.Close()

	if err = fsWatcher.Add(watcher.options.Input); err != nil {
		watcher.error(err)
	}

	go func() {
		for {

			if watcher.stopped {
				return
			}

			time.Sleep(time.Second)
			now := time.Now()

			for name, lastEvent := range watcher.recentWrites {
				diff := now.Sub(lastEvent)
				if diff > (time.Millisecond * 500) {
					delete(watcher.recentWrites, name)
					watcher.Change <- name
					go watcher.processFile(name)
				}
			}

		}
	}()

	for {
		select {
		case <-watcher.end:
			return
		case err := <-fsWatcher.Errors:
			watcher.error(err)
		case evt := <-fsWatcher.Events:
			// Ignore the event if the file is currently processed
			log.Printf("Checking blacklist: '%s'", evt.Name)
			if _, ok := watcher.blacklist[evt.Name]; ok == true {
				return
			}
			if evt.Op&fsnotify.Create == fsnotify.Create || evt.Op&fsnotify.Write == fsnotify.Write {
				watcher.recentWrites[evt.Name] = time.Now()
			}
		}
	}

}

func (watcher *Watcher) handleOriginalFile(name string) {

	var err error
	if watcher.options.Preserve {
		_, file := path.Split(name)
		err = os.Rename(name, watcher.options.Output+"/-original_0_"+basename(file))
	} else {
		err = os.Remove(name)
	}

	if err != nil {
		watcher.error(err)
	}

}

func (watcher *Watcher) error(err error) {
	watcher.Error <- err
}

func basename(name string) string {
	i := strings.LastIndex(name, string(os.PathSeparator))
	return name[i+1:]
}
<|endoftext|>


<commit_before>package aws

import (
	"bytes"
	"fmt"
	"log"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/hashicorp/terraform/helper/hashcode"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceAwsVolumeAttachment() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsVolumeAttachmentCreate,
		Read:   resourceAwsVolumeAttachmentRead,
		Delete: resourceAwsVolumeAttachmentDelete,

		Schema: map[string]*schema.Schema{
			"device_name": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"instance_id": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"volume_id": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"force_detach": {
				Type:     schema.TypeBool,
				Optional: true,
				Computed: true,
			},
			"skip_destroy": {
				Type:     schema.TypeBool,
				Optional: true,
				Computed: true,
			},
		},
	}
}

func resourceAwsVolumeAttachmentCreate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).ec2conn
	name := d.Get("device_name").(string)
	iID := d.Get("instance_id").(string)
	vID := d.Get("volume_id").(string)

	// Find out if the volume is already attached to the instance, in which case
	// we have nothing to do
	request := &ec2.DescribeVolumesInput{
		VolumeIds: []*string{aws.String(vID)},
		Filters: []*ec2.Filter{
			&ec2.Filter{
				Name:   aws.String("attachment.instance-id"),
				Values: []*string{aws.String(iID)},
			},
			&ec2.Filter{
				Name:   aws.String("attachment.device"),
				Values: []*string{aws.String(name)},
			},
		},
	}

	vols, err := conn.DescribeVolumes(request)
	if (err != nil) || (len(vols.Volumes) == 0) {
		// not attached
		opts := &ec2.AttachVolumeInput{
			Device:     aws.String(name),
			InstanceId: aws.String(iID),
			VolumeId:   aws.String(vID),
		}

		log.Printf("[DEBUG] Attaching Volume (%s) to Instance (%s)", vID, iID)
		_, err := conn.AttachVolume(opts)
		if err != nil {
			if awsErr, ok := err.(awserr.Error); ok {
				return fmt.Errorf("[WARN] Error attaching volume (%s) to instance (%s), message: \"%s\", code: \"%s\"",
					vID, iID, awsErr.Message(), awsErr.Code())
			}
			return err
		}
	}

	stateConf := &resource.StateChangeConf{
		Pending:    []string{"attaching"},
		Target:     []string{"attached"},
		Refresh:    volumeAttachmentStateRefreshFunc(conn, vID, iID),
		Timeout:    5 * time.Minute,
		Delay:      10 * time.Second,
		MinTimeout: 3 * time.Second,
	}

	_, err = stateConf.WaitForState()
	if err != nil {
		return fmt.Errorf(
			"Error waiting for Volume (%s) to attach to Instance: %s, error: %s",
			vID, iID, err)
	}

	d.SetId(volumeAttachmentID(name, vID, iID))
	return resourceAwsVolumeAttachmentRead(d, meta)
}

func volumeAttachmentStateRefreshFunc(conn *ec2.EC2, volumeID, instanceID string) resource.StateRefreshFunc {
	return func() (interface{}, string, error) {

		request := &ec2.DescribeVolumesInput{
			VolumeIds: []*string{aws.String(volumeID)},
			Filters: []*ec2.Filter{
				&ec2.Filter{
					Name:   aws.String("attachment.instance-id"),
					Values: []*string{aws.String(instanceID)},
				},
			},
		}

		resp, err := conn.DescribeVolumes(request)
		if err != nil {
			if awsErr, ok := err.(awserr.Error); ok {
				return nil, "failed", fmt.Errorf("code: %s, message: %s", awsErr.Code(), awsErr.Message())
			}
			return nil, "failed", err
		}

		if len(resp.Volumes) > 0 {
			v := resp.Volumes[0]
			for _, a := range v.Attachments {
				if a.InstanceId != nil && *a.InstanceId == instanceID {
					return a, *a.State, nil
				}
			}
		}
		// assume detached if volume count is 0
		return 42, "detached", nil
	}
}
func resourceAwsVolumeAttachmentRead(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).ec2conn

	request := &ec2.DescribeVolumesInput{
		VolumeIds: []*string{aws.String(d.Get("volume_id").(string))},
		Filters: []*ec2.Filter{
			&ec2.Filter{
				Name:   aws.String("attachment.instance-id"),
				Values: []*string{aws.String(d.Get("instance_id").(string))},
			},
		},
	}

	vols, err := conn.DescribeVolumes(request)
	if err != nil {
		if ec2err, ok := err.(awserr.Error); ok && ec2err.Code() == "InvalidVolume.NotFound" {
			d.SetId("")
			return nil
		}
		return fmt.Errorf("Error reading EC2 volume %s for instance: %s: %#v", d.Get("volume_id").(string), d.Get("instance_id").(string), err)
	}

	if len(vols.Volumes) == 0 || *vols.Volumes[0].State == "available" {
		log.Printf("[DEBUG] Volume Attachment (%s) not found, removing from state", d.Id())
		d.SetId("")
	}

	return nil
}

func resourceAwsVolumeAttachmentDelete(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).ec2conn

	if _, ok := d.GetOk("skip_destroy"); ok {
		log.Printf("[INFO] Found skip_destroy to be true, removing attachment %q from state", d.Id())
		d.SetId("")
		return nil
	}

	vID := d.Get("volume_id").(string)
	iID := d.Get("instance_id").(string)

	opts := &ec2.DetachVolumeInput{
		Device:     aws.String(d.Get("device_name").(string)),
		InstanceId: aws.String(iID),
		VolumeId:   aws.String(vID),
		Force:      aws.Bool(d.Get("force_detach").(bool)),
	}

	_, err := conn.DetachVolume(opts)
	if err != nil {
		return fmt.Errorf("Failed to detach Volume (%s) from Instance (%s): %s",
			vID, iID, err)
	}
	stateConf := &resource.StateChangeConf{
		Pending:    []string{"detaching"},
		Target:     []string{"detached"},
		Refresh:    volumeAttachmentStateRefreshFunc(conn, vID, iID),
		Timeout:    5 * time.Minute,
		Delay:      10 * time.Second,
		MinTimeout: 3 * time.Second,
	}

	log.Printf("[DEBUG] Detaching Volume (%s) from Instance (%s)", vID, iID)
	_, err = stateConf.WaitForState()
	if err != nil {
		return fmt.Errorf(
			"Error waiting for Volume (%s) to detach from Instance: %s",
			vID, iID)
	}
	d.SetId("")
	return nil
}

func volumeAttachmentID(name, volumeID, instanceID string) string {
	var buf bytes.Buffer
	buf.WriteString(fmt.Sprintf("%s-", name))
	buf.WriteString(fmt.Sprintf("%s-", instanceID))
	buf.WriteString(fmt.Sprintf("%s-", volumeID))

	return fmt.Sprintf("vai-%d", hashcode.String(buf.String()))
}
<commit_msg>Check instance is running before trying to attach (#12459)<commit_after>package aws

import (
	"bytes"
	"fmt"
	"log"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/hashicorp/terraform/helper/hashcode"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceAwsVolumeAttachment() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsVolumeAttachmentCreate,
		Read:   resourceAwsVolumeAttachmentRead,
		Delete: resourceAwsVolumeAttachmentDelete,

		Schema: map[string]*schema.Schema{
			"device_name": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"instance_id": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"volume_id": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"force_detach": {
				Type:     schema.TypeBool,
				Optional: true,
				Computed: true,
			},
			"skip_destroy": {
				Type:     schema.TypeBool,
				Optional: true,
				Computed: true,
			},
		},
	}
}

func resourceAwsVolumeAttachmentCreate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).ec2conn
	name := d.Get("device_name").(string)
	iID := d.Get("instance_id").(string)
	vID := d.Get("volume_id").(string)

	// Find out if the volume is already attached to the instance, in which case
	// we have nothing to do
	request := &ec2.DescribeVolumesInput{
		VolumeIds: []*string{aws.String(vID)},
		Filters: []*ec2.Filter{
			&ec2.Filter{
				Name:   aws.String("attachment.instance-id"),
				Values: []*string{aws.String(iID)},
			},
			&ec2.Filter{
				Name:   aws.String("attachment.device"),
				Values: []*string{aws.String(name)},
			},
		},
	}

	vols, err := conn.DescribeVolumes(request)
	if (err != nil) || (len(vols.Volumes) == 0) {
		// This handles the situation where the instance is created by
		// a spot request and whilst the request has been fulfilled the
		// instance is not running yet
		stateConf := &resource.StateChangeConf{
			Pending:    []string{"pending"},
			Target:     []string{"running"},
			Refresh:    InstanceStateRefreshFunc(conn, iID),
			Timeout:    10 * time.Minute,
			Delay:      10 * time.Second,
			MinTimeout: 3 * time.Second,
		}

		_, err = stateConf.WaitForState()
		if err != nil {
			return fmt.Errorf(
				"Error waiting for instance (%s) to become ready: %s",
				iID, err)
		}

		// not attached
		opts := &ec2.AttachVolumeInput{
			Device:     aws.String(name),
			InstanceId: aws.String(iID),
			VolumeId:   aws.String(vID),
		}

		log.Printf("[DEBUG] Attaching Volume (%s) to Instance (%s)", vID, iID)
		_, err := conn.AttachVolume(opts)
		if err != nil {
			if awsErr, ok := err.(awserr.Error); ok {
				return fmt.Errorf("[WARN] Error attaching volume (%s) to instance (%s), message: \"%s\", code: \"%s\"",
					vID, iID, awsErr.Message(), awsErr.Code())
			}
			return err
		}
	}

	stateConf := &resource.StateChangeConf{
		Pending:    []string{"attaching"},
		Target:     []string{"attached"},
		Refresh:    volumeAttachmentStateRefreshFunc(conn, vID, iID),
		Timeout:    5 * time.Minute,
		Delay:      10 * time.Second,
		MinTimeout: 3 * time.Second,
	}

	_, err = stateConf.WaitForState()
	if err != nil {
		return fmt.Errorf(
			"Error waiting for Volume (%s) to attach to Instance: %s, error: %s",
			vID, iID, err)
	}

	d.SetId(volumeAttachmentID(name, vID, iID))
	return resourceAwsVolumeAttachmentRead(d, meta)
}

func volumeAttachmentStateRefreshFunc(conn *ec2.EC2, volumeID, instanceID string) resource.StateRefreshFunc {
	return func() (interface{}, string, error) {

		request := &ec2.DescribeVolumesInput{
			VolumeIds: []*string{aws.String(volumeID)},
			Filters: []*ec2.Filter{
				&ec2.Filter{
					Name:   aws.String("attachment.instance-id"),
					Values: []*string{aws.String(instanceID)},
				},
			},
		}

		resp, err := conn.DescribeVolumes(request)
		if err != nil {
			if awsErr, ok := err.(awserr.Error); ok {
				return nil, "failed", fmt.Errorf("code: %s, message: %s", awsErr.Code(), awsErr.Message())
			}
			return nil, "failed", err
		}

		if len(resp.Volumes) > 0 {
			v := resp.Volumes[0]
			for _, a := range v.Attachments {
				if a.InstanceId != nil && *a.InstanceId == instanceID {
					return a, *a.State, nil
				}
			}
		}
		// assume detached if volume count is 0
		return 42, "detached", nil
	}
}
func resourceAwsVolumeAttachmentRead(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).ec2conn

	request := &ec2.DescribeVolumesInput{
		VolumeIds: []*string{aws.String(d.Get("volume_id").(string))},
		Filters: []*ec2.Filter{
			&ec2.Filter{
				Name:   aws.String("attachment.instance-id"),
				Values: []*string{aws.String(d.Get("instance_id").(string))},
			},
		},
	}

	vols, err := conn.DescribeVolumes(request)
	if err != nil {
		if ec2err, ok := err.(awserr.Error); ok && ec2err.Code() == "InvalidVolume.NotFound" {
			d.SetId("")
			return nil
		}
		return fmt.Errorf("Error reading EC2 volume %s for instance: %s: %#v", d.Get("volume_id").(string), d.Get("instance_id").(string), err)
	}

	if len(vols.Volumes) == 0 || *vols.Volumes[0].State == "available" {
		log.Printf("[DEBUG] Volume Attachment (%s) not found, removing from state", d.Id())
		d.SetId("")
	}

	return nil
}

func resourceAwsVolumeAttachmentDelete(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).ec2conn

	if _, ok := d.GetOk("skip_destroy"); ok {
		log.Printf("[INFO] Found skip_destroy to be true, removing attachment %q from state", d.Id())
		d.SetId("")
		return nil
	}

	vID := d.Get("volume_id").(string)
	iID := d.Get("instance_id").(string)

	opts := &ec2.DetachVolumeInput{
		Device:     aws.String(d.Get("device_name").(string)),
		InstanceId: aws.String(iID),
		VolumeId:   aws.String(vID),
		Force:      aws.Bool(d.Get("force_detach").(bool)),
	}

	_, err := conn.DetachVolume(opts)
	if err != nil {
		return fmt.Errorf("Failed to detach Volume (%s) from Instance (%s): %s",
			vID, iID, err)
	}
	stateConf := &resource.StateChangeConf{
		Pending:    []string{"detaching"},
		Target:     []string{"detached"},
		Refresh:    volumeAttachmentStateRefreshFunc(conn, vID, iID),
		Timeout:    5 * time.Minute,
		Delay:      10 * time.Second,
		MinTimeout: 3 * time.Second,
	}

	log.Printf("[DEBUG] Detaching Volume (%s) from Instance (%s)", vID, iID)
	_, err = stateConf.WaitForState()
	if err != nil {
		return fmt.Errorf(
			"Error waiting for Volume (%s) to detach from Instance: %s",
			vID, iID)
	}
	d.SetId("")
	return nil
}

func volumeAttachmentID(name, volumeID, instanceID string) string {
	var buf bytes.Buffer
	buf.WriteString(fmt.Sprintf("%s-", name))
	buf.WriteString(fmt.Sprintf("%s-", instanceID))
	buf.WriteString(fmt.Sprintf("%s-", volumeID))

	return fmt.Sprintf("vai-%d", hashcode.String(buf.String()))
}
<|endoftext|>