<commit_before>package util

import (
	"fmt"
	"log"
	"reflect"
	"time"

	api "github.com/armon/consul-api"
)

const (
	// The amount of time to do a blocking query for
	defaultWaitTime = 60 * time.Second

	// The amount of time to wait when Consul returns an error
	defaultRetry = 1 * time.Second
)

// View is a representation of a Dependency and the most recent data it has
// received from Consul.
type View struct {
	Dependency Dependency

	Data         interface{}
	receivedData bool
	lastIndex    uint64

	client *api.Client
}

// NewView creates a new view object from the given Consul API client and
// Dependency. If an error occurs, it will be returned.
func NewView(client *api.Client, dep Dependency) (*View, error) {
	if client == nil {
		return nil, fmt.Errorf("view: missing Consul API client")
	}

	if dep == nil {
		return nil, fmt.Errorf("view: missing Dependency")
	}

	return &View{
		client:     client,
		Dependency: dep,
	}, nil
}

// poll queries the Consul instance for data using the fetch function, but also
// accounts for interrupts on the interrupt channel. This allows the poll
// function to be fired in a goroutine, but then halted even if the fetch
// function is in the middle of a blocking query.
func (v *View) poll(once bool, viewCh chan<- *View,
	errCh chan<- error, stopCh <-chan struct{}, retryFunc RetryFunc) {
	currentRetry := defaultRetry

	for {
		doneCh, fetchErrCh := make(chan struct{}, 1), make(chan error, 1)
		go v.fetch(doneCh, fetchErrCh)

		select {
		case <-doneCh:
			// Reset the retry to avoid exponentially incrementing retries when we
			// have some successful requests
			currentRetry = defaultRetry

			log.Printf("[INFO] (%s) received data from consul", v.display())
			viewCh <- v

			// If we are operating in once mode, do not loop - we received data at
			// least once which is the API promise here.
			if once {
				return
			}
		case err := <-fetchErrCh:
			log.Printf("[ERR] (%s) %s", v.display(), err)

			// Intentionally do not send the error back up to the watcher and just
			// retry. Eventually, once Consul API implements errwrap and multierror,
			// we can check the "type" of error and conditionally alert back, but for
			// now we will just swallow the error and continue.
			// if err.Contains(Something) {
			//   errCh <- err
			// }

			// Sleep and retry
			currentRetry = retryFunc(currentRetry)
			time.Sleep(currentRetry)
			continue
		case <-stopCh:
			log.Printf("[DEBUG] (%s) stopping poll (received on stopCh)", v.display())
			return
		}
	}
}

// fetch queries the Consul instance for the attached dependency. This API
// promises that either data will be written to doneCh or an error will be
// written to errCh. It is designed to be run in a goroutine that selects the
// result of doneCh and errCh. It is assumed that only one instance of fetch
// is running per View and therefore no locking or mutexes are used.
func (v *View) fetch(doneCh chan struct{}, errCh chan<- error) {
	log.Printf("[DEBUG] (%s) starting fetch", v.display())

	for {
		options := &api.QueryOptions{
			WaitTime:  defaultWaitTime,
			WaitIndex: v.lastIndex,
		}
		data, qm, err := v.Dependency.Fetch(v.client, options)
		if err != nil {
			errCh <- err
			return
		}

		if qm == nil {
			errCh <- fmt.Errorf("consul returned nil qm; this should never happen" +
				"and is probably a bug in consul-template or consulapi")
			return
		}

		if qm.LastIndex == v.lastIndex {
			log.Printf("[DEBUG] (%s) no new data (index was the same)", v.display())
			continue
		}

		v.lastIndex = qm.LastIndex

		if v.receivedData && reflect.DeepEqual(data, v.Data) {
			log.Printf("[DEBUG] (%s) no new data (contents were the same)", v.display())
			continue
		}

		v.Data = data
		v.receivedData = true
		close(doneCh)
		return
	}
}

// display returns a string that represents this view.
func (v *View) display() string {
	return v.Dependency.Display()
}
<commit_msg>Make doneCh readonly<commit_after>package util

import (
	"fmt"
	"log"
	"reflect"
	"time"

	api "github.com/armon/consul-api"
)

const (
	// The amount of time to do a blocking query for
	defaultWaitTime = 60 * time.Second

	// The amount of time to wait when Consul returns an error
	defaultRetry = 1 * time.Second
)

// View is a representation of a Dependency and the most recent data it has
// received from Consul.
type View struct {
	Dependency Dependency

	Data         interface{}
	receivedData bool
	lastIndex    uint64

	client *api.Client
}

// NewView creates a new view object from the given Consul API client and
// Dependency. If an error occurs, it will be returned.
func NewView(client *api.Client, dep Dependency) (*View, error) {
	if client == nil {
		return nil, fmt.Errorf("view: missing Consul API client")
	}

	if dep == nil {
		return nil, fmt.Errorf("view: missing Dependency")
	}

	return &View{
		client:     client,
		Dependency: dep,
	}, nil
}

// poll queries the Consul instance for data using the fetch function, but also
// accounts for interrupts on the interrupt channel. This allows the poll
// function to be fired in a goroutine, but then halted even if the fetch
// function is in the middle of a blocking query.
func (v *View) poll(once bool, viewCh chan<- *View,
	errCh chan<- error, stopCh <-chan struct{}, retryFunc RetryFunc) {
	currentRetry := defaultRetry

	for {
		doneCh, fetchErrCh := make(chan struct{}, 1), make(chan error, 1)
		go v.fetch(doneCh, fetchErrCh)

		select {
		case <-doneCh:
			// Reset the retry to avoid exponentially incrementing retries when we
			// have some successful requests
			currentRetry = defaultRetry

			log.Printf("[INFO] (%s) received data from consul", v.display())
			viewCh <- v

			// If we are operating in once mode, do not loop - we received data at
			// least once which is the API promise here.
			if once {
				return
			}
		case err := <-fetchErrCh:
			log.Printf("[ERR] (%s) %s", v.display(), err)

			// Intentionally do not send the error back up to the watcher and just
			// retry. Eventually, once Consul API implements errwrap and multierror,
			// we can check the "type" of error and conditionally alert back, but for
			// now we will just swallow the error and continue.
			// if err.Contains(Something) {
			//   errCh <- err
			// }

			// Sleep and retry
			currentRetry = retryFunc(currentRetry)
			time.Sleep(currentRetry)
			continue
		case <-stopCh:
			log.Printf("[DEBUG] (%s) stopping poll (received on stopCh)", v.display())
			return
		}
	}
}

// fetch queries the Consul instance for the attached dependency. This API
// promises that either data will be written to doneCh or an error will be
// written to errCh. It is designed to be run in a goroutine that selects the
// result of doneCh and errCh. It is assumed that only one instance of fetch
// is running per View and therefore no locking or mutexes are used.
func (v *View) fetch(doneCh chan<- struct{}, errCh chan<- error) {
	log.Printf("[DEBUG] (%s) starting fetch", v.display())

	for {
		options := &api.QueryOptions{
			WaitTime:  defaultWaitTime,
			WaitIndex: v.lastIndex,
		}
		data, qm, err := v.Dependency.Fetch(v.client, options)
		if err != nil {
			errCh <- err
			return
		}

		if qm == nil {
			errCh <- fmt.Errorf("consul returned nil qm; this should never happen" +
				"and is probably a bug in consul-template or consulapi")
			return
		}

		if qm.LastIndex == v.lastIndex {
			log.Printf("[DEBUG] (%s) no new data (index was the same)", v.display())
			continue
		}

		v.lastIndex = qm.LastIndex

		if v.receivedData && reflect.DeepEqual(data, v.Data) {
			log.Printf("[DEBUG] (%s) no new data (contents were the same)", v.display())
			continue
		}

		v.Data = data
		v.receivedData = true
		close(doneCh)
		return
	}
}

// display returns a string that represents this view.
func (v *View) display() string {
	return v.Dependency.Display()
}
<|endoftext|>


<commit_before>package uuid

import (
	"testing"
)

var (
	testStringUUID = "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11"
	testByteUUID   = []byte("a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11")
)

func TestZero(t *testing.T) {
	u := UUID{}

	if !u.IsZero() {
		t.Error("Default value is not zero")
	}
}

func TestZero2(t *testing.T) {
	u := UUID{}

	if u.String() != "00000000-0000-0000-0000-000000000000" {
		t.Error("Default zero value is not zero uuid")
	}
}

func TestFromString(t *testing.T) {
	u, err := FromString("00000000-0000-0000-0000-000000000000")
	if err != nil {
		t.Error("Failed to read zero-uuid")
	}

	if !u.IsZero() {
		t.Error("Zero-uuid from string does not yield zero-uuid")
	}

	if u.String() != "00000000-0000-0000-0000-000000000000" {
		t.Error("zero uuid is not zero uuid")
	}
}

func TestFromStringMultiple(t *testing.T) {
	list := []string{
		"00000000-0000-0000-0000-00000000000f",
		"00000000-0000-0000-0000-0000000000f0",
		"00000000-0000-0000-0000-100000000000",
		"00000000-0000-0000-f000-000000000000",
		"00000000-0000-f000-0000-000000000000",
		"00000000-f000-0000-0000-000000000000",
		"f0000000-0000-0000-0000-000000000000",
		"0f000000-0000-0000-0000-000000000000",
		"000f0000-0000-0000-0000-000000000000",
		"00000000-00f0-0000-0000-000000000000",
		"f0000000-f000-f000-f000-f00000000000",
		"12345678-9abc-deff-edcb-a98765432100",
		"ffffffff-ffff-ffff-ffff-ffffffffffff",
	}

	for _, i := range list {
		u, err := FromString(i)
		if err != nil {
			t.Error("Failed to read uuid '" + i + "':" + err.Error())

			continue
		}

		if u.String() != i {
			t.Error("String representation of UUID '" + i + "' does not match, got '" + u.String() + "'.")
		}
	}
}

func TestMaybeFromString(t *testing.T) {
	list := map[string]string{
		"00000000-0000-0000-0000-00000000000f": "00000000-0000-0000-0000-00000000000f",
		"12345678-1234-5678-1234-567812345678": "12345678-1234-5678-1234-567812345678",
		"c56a4180-65aa-42ec-a945-5fd21dec0538": "c56a4180-65aa-42ec-a945-5fd21dec0538",
		"00000000-0000-0000-0000-00000000000":  "00000000-0000-0000-0000-000000000000",
		"":                                     "00000000-0000-0000-0000-000000000000",
		"C56A4180-65AA-42EC-A945-5FD21DEC":     "00000000-0000-0000-0000-000000000000",
		"x56a4180-h5aa-42ec-a945-5fd21dec0538": "00000000-0000-0000-0000-000000000000",
		"null":                                 "00000000-0000-0000-0000-000000000000",
		"something invalid here":               "00000000-0000-0000-0000-000000000000",
	}

	for input, expected := range list {
		u := MaybeFromString(input)
		if u.String() != expected {
			t.Errorf("MaybeFromString(%s) does not match %s, got %s", input, expected, u.String())
		}
	}
}

func TestMustFromString(t *testing.T) {
	list := map[string]string{
		"00000000-0000-0000-0000-00000000000f": "00000000-0000-0000-0000-00000000000f",
		"12345678-1234-5678-1234-567812345678": "12345678-1234-5678-1234-567812345678",
		"c56a4180-65aa-42ec-a945-5fd21dec0538": "c56a4180-65aa-42ec-a945-5fd21dec0538",
		"00000000-0000-0000-0000-00000000000":  "", /* Means panic is expected */
		"":                                     "",
		"C56A4180-65AA-42EC-A945-5FD21DEC":     "",
		"x56a4180-h5aa-42ec-a945-5fd21dec0538": "",
		"null":                                 "",
		"something invalid here":               "",
	}

	for input, expected := range list {
		testMustFromString(t, input, expected)
	}
}

func testMustFromString(t *testing.T, input string, expected string) {
	var u UUID

	defer func() {
		err := recover()

		if expected == "" && err == nil {
			t.Errorf("MustFromString(%s) expected panic, got %s", input, u.String())
		}

		if expected != "" && err != nil {
			t.Errorf("MustFromString(%s) paniced, expected %s", input, expected)
		}
	}()

	u = MustFromString(input)
	if expected != "" && u.String() != expected {
		t.Errorf("MustFromString(%s) does not match %s, got %s", input, expected, u.String())
	}
}

func TestAlternativeValid(t *testing.T) {
	uuid := "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11"
	list := []string{
		"A0EEBC99-9C0B-4EF8-BB6D-6BB9BD380A11",
		"{a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11}",
		"a0eebc999c0b4ef8bb6d6bb9bd380a11",
		"a0ee-bc99-9c0b-4ef8-bb6d-6bb9-bd38-0a11",
		"{a0eebc99-9c0b4ef8-bb6d6bb9-bd380a11}",
	}

	for _, i := range list {
		u, err := FromString(i)
		if err != nil {
			t.Error("Failed to read uuid '" + i + "':" + err.Error())

			continue
		}

		if u.String() != uuid {
			t.Error("String representation of UUID '" + i + "' does not match, got '" + u.String() + "'.")
		}
	}
}

func TestInvalid(t *testing.T) {
	list := []string{
		"",
		"0",
		"abcdefg",
		"fffffff-ffff-ffff-ffff-fffffffffffff",   /* all - shifted one to the left */
		"fffffffff-ffff-ffff-ffff-fffffffffff",   /* all - shifted one to the right */
		"ffffffff-ffff-ffff-ffff-fffffffffff",    /* one char too short */
		"ffffffff-ffff-ffff-ffff-fffffffffffff",  /* one char too long */
		"ffffffff-ffff-ffff-ffff-ffffffffffffff", /* two char too long */
		"ffffffff-ffff-ffff-ffff--fffffffffff",   /* one dash instead of char */
	}

	for _, i := range list {
		_, err := FromString(i)
		if err == nil {
			t.Error("Expected error but got nothing when reading uuid '" + i + "'.")
		}
	}
}

func TestV4(t *testing.T) {
	u, err := V4()
	if err != nil {
		panic(err)
	}

	if u[6]&0xf0 != 0x40 {
		t.Error("UUID generated from V4() does not have the version byte set to 4: '" + u.String() + "'.")
	}

	if u[8]&0xf0 < 0x80 || u[8]&0xf0 > 0xB0 {
		t.Error("UUID generated from V4() does not have the 9th byte beginning with 8, 9, A or B: '" + u.String() + "'.")
	}
}

func TestSetZero(t *testing.T) {
	u, err := FromString("12345678-9abc-deff-edcb-a98765432100")
	if err != nil {
		panic(err)
	}

	if u == [16]byte{} {
		panic("zero UUID returned from FromString")
	}

	u.SetZero()

	if u != [16]byte{} {
		t.Errorf("SetZero() did not zero UUID")
	}
}

func BenchmarkFromString(b *testing.B) {
	for i := 0; i < b.N; i++ {
		FromString("12345678-9abc-deff-edcb-a98765432100")
	}
}

func BenchmarkFromString2(b *testing.B) {
	for i := 0; i < b.N; i++ {
		FromString("123456789abcdeffedcba98765432100")
	}
}

func BenchmarkSetString(b *testing.B) {
	for i := 0; i < b.N; i++ {
		u := UUID{}

		u.SetString(testStringUUID)
	}
}

func BenchmarkReadBytes(b *testing.B) {
	for i := 0; i < b.N; i++ {
		u := UUID{}

		u.ReadBytes(testByteUUID)
	}
}

func BenchmarkString(b *testing.B) {
	u, err := FromString("a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11")
	if err != nil {
		panic(err)
	}

	for i := 0; i < b.N; i++ {
		u.String()
	}
}

func BenchmarkV4(b *testing.B) {
	for i := 0; i < b.N; i++ {
		V4()
	}
}

func BenchmarkMaybeFromStringOk(b *testing.B) {
	for i := 0; i < b.N; i++ {
		MaybeFromString("a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11")
	}
}

func BenchmarkMaybeFromStringFail(b *testing.B) {
	for i := 0; i < b.N; i++ {
		MaybeFromString("a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11abcdef")
	}
}
<commit_msg>Tests: Refactored basic tests to use the same list of UUIDs for all parse-methods<commit_after>package uuid

import (
	"fmt"
	"testing"
)

var (
	testStringUUID = "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11"
	testZeroString = "00000000-0000-0000-0000-000000000000"
	testByteUUID   = []byte("a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11")
	testMixed      = map[string]validator{
		"00000000-0000-0000-0000-00000000000f": valid{"00000000-0000-0000-0000-00000000000f"},
		"00000000-0000-0000-0000-0000000000f0": valid{"00000000-0000-0000-0000-0000000000f0"},
		"00000000-0000-0000-0000-100000000000": valid{"00000000-0000-0000-0000-100000000000"},
		"00000000-0000-0000-f000-000000000000": valid{"00000000-0000-0000-f000-000000000000"},
		"00000000-0000-f000-0000-000000000000": valid{"00000000-0000-f000-0000-000000000000"},
		"00000000-f000-0000-0000-000000000000": valid{"00000000-f000-0000-0000-000000000000"},
		"f0000000-0000-0000-0000-000000000000": valid{"f0000000-0000-0000-0000-000000000000"},
		"0f000000-0000-0000-0000-000000000000": valid{"0f000000-0000-0000-0000-000000000000"},
		"000f0000-0000-0000-0000-000000000000": valid{"000f0000-0000-0000-0000-000000000000"},
		"00000000-00f0-0000-0000-000000000000": valid{"00000000-00f0-0000-0000-000000000000"},
		"f0000000-f000-f000-f000-f00000000000": valid{"f0000000-f000-f000-f000-f00000000000"},
		"12345678-9abc-deff-edcb-a98765432100": valid{"12345678-9abc-deff-edcb-a98765432100"},
		"ffffffff-ffff-ffff-ffff-ffffffffffff": valid{"ffffffff-ffff-ffff-ffff-ffffffffffff"},
		"12345678-1234-5678-1234-567812345678": valid{"12345678-1234-5678-1234-567812345678"},
		"c56a4180-65aa-42ec-a945-5fd21dec0538": valid{"c56a4180-65aa-42ec-a945-5fd21dec0538"},
		"00000000-0000-0000-0000-000000000000": valid{"00000000-0000-0000-0000-000000000000"},
		"00000000-0000-0000-0000-00000000000":  invalid{},
		"":        invalid{},
		"0":       invalid{},
		"abcdefg": invalid{},
		"fffffff-ffff-ffff-ffff-fffffffffffff":   invalid{}, /* all - shifted one to the left */
		"fffffffff-ffff-ffff-ffff-fffffffffff":   invalid{}, /* all - shifted one to the right */
		"ffffffff-ffff-ffff-ffff-fffffffffff":    invalid{}, /* one char too short */
		"ffffffff-ffff-ffff-ffff-fffffffffffff":  invalid{}, /* one char too long */
		"ffffffff-ffff-ffff-ffff-ffffffffffffff": invalid{}, /* two char too long */
		"ffffffff-ffff-ffff-ffff--fffffffffff":   invalid{}, /* one dash instead of char */
		"C56A4180-65AA-42EC-A945-5FD21DEC":       invalid{},
		"x56a4180-h5aa-42ec-a945-5fd21dec0538":   invalid{},
		"null": invalid{},
		"something invalid here":                  invalid{},
		"A0EEBC99-9C0B-4EF8-BB6D-6BB9BD380A11":    valid{"a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11"},
		"{a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11}":  valid{"a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11"},
		"a0eebc999c0b4ef8bb6d6bb9bd380a11":        valid{"a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11"},
		"a0ee-bc99-9c0b-4ef8-bb6d-6bb9-bd38-0a11": valid{"a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11"},
		"{a0eebc99-9c0b4ef8-bb6d6bb9-bd380a11}":   valid{"a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11"},
	}
)

// validator validates errors and UUIDs for tests
type validator interface {
	ReadError(error) error
	Validate(UUID) error
	ValidateEmpty(UUID) error
}

// valid is an expected valid UUID read.
type valid struct {
	Str string
}

// ReadError validates any errors which might arise when reading the UUID.
func (v valid) ReadError(err error) error {
	if err != nil {
		return fmt.Errorf("expected '%s', got error: %s", v.Str, err.Error())
	}

	return nil
}

// Validate validates a UUID with the expectation that it should not validate
// upon read failure.
func (v valid) Validate(u UUID) error {
	if v.Str != u.String() {
		return fmt.Errorf("string representation '%s' does not match '%s'.", u.String(), v.Str)
	}

	return nil
}

// ValidateEmpty validates a UUID where an error condition must set the UUID
// to zero.
func (v valid) ValidateEmpty(u UUID) error {
	return v.Validate(u)
}

// invalid is an expected failure to read a UUID.
type invalid struct{}

// ReadError validates any errors which might arise when reading the UUID.
func (v invalid) ReadError(err error) error {
	if err == nil {
		return fmt.Errorf("expected error")
	}

	return nil
}

// Validate validates a UUID with the expectation that it should not validate
// upon read failure.
func (v invalid) Validate(u UUID) error {
	return nil
}

// ValidateEmpty validates a UUID where an error condition must set the UUID
// to zero.
func (v invalid) ValidateEmpty(u UUID) error {
	if u != [16]byte{} {
		return fmt.Errorf("expected empty UUID, got '%s'", u.String())
	}

	return nil
}

func TestZero(t *testing.T) {
	u := UUID{}

	if !u.IsZero() {
		t.Error("Default value is not zero")
	}
}

func TestZero2(t *testing.T) {
	u := UUID{}

	if u.String() != testZeroString {
		t.Error("Default zero value is not zero uuid")
	}
}

func TestFromString(t *testing.T) {
	for i, v := range testMixed {
		u, err := FromString(i)
		if e := v.ReadError(err); e != nil {
			t.Errorf("FromString(%s): %s", i, e.Error())
		} else if e := v.Validate(u); e != nil {
			t.Errorf("FromString(%s): %s", i, e.Error())
		}
	}
}

func TestFromStringZero(t *testing.T) {
	u, err := FromString(testZeroString)
	if err != nil {
		t.Error("Failed to read zero-uuid")
	}

	if !u.IsZero() {
		t.Error("Zero-uuid from string does not yield zero-uuid")
	}

	if u.String() != testZeroString {
		t.Error("zero uuid is not zero uuid")
	}
}

func TestReadBytes(t *testing.T) {
	for i, v := range testMixed {
		u := UUID{}

		err := u.ReadBytes([]byte(i))
		if e := v.ReadError(err); e != nil {
			t.Errorf("FromString(%s): %s", i, e.Error())
		} else if e := v.Validate(u); e != nil {
			t.Errorf("FromString(%s): %s", i, e.Error())
		}
	}
}

func TestSetString(t *testing.T) {
	for i, v := range testMixed {
		u := UUID{}

		err := u.SetString(i)
		if e := v.ReadError(err); e != nil {
			t.Errorf("SetString(%s): %s", i, e.Error())
		} else if e := v.Validate(u); e != nil {
			t.Errorf("SetString(%s): %s", i, e.Error())
		}
	}
}

func TestMaybeFromString(t *testing.T) {
	for i, v := range testMixed {
		u := MaybeFromString(i)
		if e := v.ValidateEmpty(u); e != nil {
			t.Errorf("MaybeFromString(%s): %s", i, e.Error())
		}
	}
}

func TestMustFromString(t *testing.T) {
	for i, v := range testMixed {
		testMustFromString(t, i, v)
	}
}

func testMustFromString(t *testing.T, i string, v validator) {
	var u UUID

	defer func() {
		r := recover()

		err, ok := r.(error)
		if !ok && r != nil {
			panic(fmt.Sprintf("in test for MustString: got non-error: %+v", r))
		}

		if e := v.ReadError(err); e != nil {
			t.Errorf("FromString(%s): %s", i, e.Error())
		}
	}()

	u = MustFromString(i)
	if e := v.Validate(u); e != nil {
		t.Errorf("MustFromString(%s): %s", i, e.Error())
	}
}

func TestV4(t *testing.T) {
	u, err := V4()
	if err != nil {
		panic(err)
	}

	if u[6]&0xf0 != 0x40 {
		t.Errorf("UUID generated from V4() does not have the version byte set to 4: '%s'.", u.String())
	}

	if u[8]&0xf0 < 0x80 || u[8]&0xf0 > 0xB0 {
		t.Errorf("UUID generated from V4() does not have the 9th byte beginning with 8, 9, A or B: '%s'.", u.String())
	}
}

func TestSetZero(t *testing.T) {
	u, err := FromString("12345678-9abc-deff-edcb-a98765432100")
	if err != nil {
		panic(err)
	}

	if u == [16]byte{} {
		panic("zero UUID returned from FromString")
	}

	u.SetZero()

	if u != [16]byte{} {
		t.Errorf("SetZero() did not zero UUID")
	}
}

func BenchmarkFromString(b *testing.B) {
	for i := 0; i < b.N; i++ {
		FromString("12345678-9abc-deff-edcb-a98765432100")
	}
}

func BenchmarkFromString2(b *testing.B) {
	for i := 0; i < b.N; i++ {
		FromString("123456789abcdeffedcba98765432100")
	}
}

func BenchmarkSetString(b *testing.B) {
	for i := 0; i < b.N; i++ {
		u := UUID{}

		u.SetString(testStringUUID)
	}
}

func BenchmarkReadBytes(b *testing.B) {
	for i := 0; i < b.N; i++ {
		u := UUID{}

		u.ReadBytes(testByteUUID)
	}
}

func BenchmarkString(b *testing.B) {
	u, err := FromString("a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11")
	if err != nil {
		panic(err)
	}

	for i := 0; i < b.N; i++ {
		u.String()
	}
}

func BenchmarkV4(b *testing.B) {
	for i := 0; i < b.N; i++ {
		V4()
	}
}

func BenchmarkMaybeFromStringOk(b *testing.B) {
	for i := 0; i < b.N; i++ {
		MaybeFromString("a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11")
	}
}

func BenchmarkMaybeFromStringFail(b *testing.B) {
	for i := 0; i < b.N; i++ {
		MaybeFromString("a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11abcdef")
	}
}
<|endoftext|>


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

package policy

import (
	"errors"
	"testing"
	"time"

	"golang.org/x/net/context"
)

func fakePolicyHandler(ctx context.Context, p Policy) (<-chan Event, error) {
	foo, ok := p.M["foo"]
	if !ok {
		return nil, errors.New(`"foo" key missing in fake policy`)
	}
	interval, ok := p.M["interval"]
	if !ok {
		return nil, errors.New(`"interval" key missing in fake policy`)
	}
	d, err := time.ParseDuration(interval)
	if err != nil {
		return nil, err
	}

	// This check is here to ensure time.Ticker(d) doesn't panic
	if d <= 0 {
		return nil, errors.New("interval must be a positive quantity")
	}

	out := make(chan Event)
	go func() {
		t := time.NewTicker(d)
		for {
			select {
			case <-ctx.Done():
				t.Stop()
				close(out)
				return
			case <-t.C:
				out <- Event{
					Time:   time.Now(),
					Policy: p,
					Data: map[string]interface{}{
						"foo": foo,
					},
				}
			}
		}
	}()
	return out, nil
}

func TestRegisterHandler(t *testing.T) {
	err := RegisterHandler("", fakePolicyHandler)
	if err == nil {
		t.Fatal(errors.New("NewHandler should return an error when the type is empty"))
	}
	err = RegisterHandler("fake", fakePolicyHandler)
	if err != nil {
		t.Fatal(err)
	}
}

func TestValid(t *testing.T) {
	p := Policy{
		Name: "",
	}
	if err := p.Valid(); err == nil {
		t.Error(`want error "policy name can't be empty" got nil`)
	}
	p = Policy{
		Name: "dummy",
		Type: "unknownDummyType",
	}
	if err := p.Valid(); err == nil {
		t.Error(`want error "policy type unknown" got nil`)
	}
}

func TestExecute(t *testing.T) {
	p := Policy{
		Name: "dummy",
		Type: "fake",
		M: map[string]string{
			"foo":      "foo_value",
			"interval": "200ms",
		},
	}
	ctx, cancel := context.WithCancel(context.Background())
	out, err := p.Execute(ctx)
	if err != nil {
		t.Error(err)
	}
	go func() {
		time.Sleep(1 * time.Second)
		cancel()
	}()

	var count int
	// Here, we are also able to test whether out is being
	// closed when cancel() is called. If out is not closed,
	// this test should have been running forever.
	for evt := range out {
		count++
		if evt.Data["foo"] != "foo_value" {
			t.Errorf(`want evt.Data["foo"] = %s; got %s`, "foo", evt.Data["foo"])
		}
	}

	// The interval for the dummy policy is 200ms.
	// We are calling cancel after 1 sec. Typically, we receive
	// 4 or 5 events in that duration.
	if count != 4 && count != 5 {
		t.Errorf(`want count to be either 4 or 5; got %d`, count)
	}
}
<commit_msg>policy: add more tests<commit_after>// Copyright 2015 CodeIgnition. All rights reserved.
// Use of this source code is governed by a BSD
// license that can be found in the LICENSE file.

package policy

import (
	"errors"
	"testing"
	"time"

	"golang.org/x/net/context"
)

func fakePolicyHandler(ctx context.Context, p Policy) (<-chan Event, error) {
	foo, ok := p.M["foo"]
	if !ok {
		return nil, errors.New(`"foo" key missing in fake policy`)
	}
	interval, ok := p.M["interval"]
	if !ok {
		return nil, errors.New(`"interval" key missing in fake policy`)
	}
	d, err := time.ParseDuration(interval)
	if err != nil {
		return nil, err
	}

	// This check is here to ensure time.Ticker(d) doesn't panic
	if d <= 0 {
		return nil, errors.New("interval must be a positive quantity")
	}

	out := make(chan Event)
	go func() {
		t := time.NewTicker(d)
		for {
			select {
			case <-ctx.Done():
				t.Stop()
				close(out)
				return
			case <-t.C:
				out <- Event{
					Time:   time.Now(),
					Policy: p,
					Data: map[string]interface{}{
						"foo": foo,
					},
				}
			}
		}
	}()
	return out, nil
}

func TestRegisterHandler(t *testing.T) {
	err := RegisterHandler("", fakePolicyHandler)
	if err == nil {
		t.Fatal(errors.New("NewHandler should return an error when the type is empty"))
	}
	err = RegisterHandler("fake", fakePolicyHandler)
	if err != nil {
		t.Fatal(err)
	}

	// test registering twice
	err = RegisterHandler("fake", fakePolicyHandler)
	if err == nil {
		t.Fatal(`want error "handler for the policy type already exists"; got nil`)
	}
}

func TestValid(t *testing.T) {
	p := Policy{
		Name: "",
	}
	if err := p.Valid(); err == nil {
		t.Error(`want error "policy name can't be empty"; got nil`)
	}
	p = Policy{
		Name: "dummy",
		Type: "unknownDummyType",
	}
	if err := p.Valid(); err == nil {
		t.Error(`want error "policy type unknown"; got nil`)
	}
}

func TestExecuteInvalidPolicy(t *testing.T) {
	p := Policy{
		Name: "dummy",
		Type: "unknownDummyType",
	}
	if _, err := p.Execute(context.TODO()); err == nil {
		t.Error(`want error "policy type unknown"; got nil`)
	}
}

func TestExecute(t *testing.T) {
	p := Policy{
		Name: "dummy",
		Type: "fake",
		M: map[string]string{
			"foo":      "foo_value",
			"interval": "200ms",
		},
	}
	ctx, cancel := context.WithCancel(context.Background())
	out, err := p.Execute(ctx)
	if err != nil {
		t.Error(err)
	}
	go func() {
		time.Sleep(1 * time.Second)
		cancel()
	}()

	var count int
	// Here, we are also able to test whether out is being
	// closed when cancel() is called. If out is not closed,
	// this test should have been running forever.
	for evt := range out {
		count++
		if evt.Data["foo"] != "foo_value" {
			t.Errorf(`want evt.Data["foo"] = %s; got %s`, "foo", evt.Data["foo"])
		}
	}

	// The interval for the dummy policy is 200ms.
	// We are calling cancel after 1 sec. Typically, we receive
	// 4 or 5 events in that duration.
	if count != 4 && count != 5 {
		t.Errorf(`want count to be either 4 or 5; got %d`, count)
	}
}
<|endoftext|>


<commit_before>package adapter

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/httputil"
	"strings"

	"github.com/cf-platform-eng/mongodb-on-demand-release/src/mongodb-service-adapter/digest"
	"github.com/tidwall/gjson"
)

type OMClient struct {
	Url      string
	Username string
	ApiKey   string
}

type Automation struct {
	MongoDbVersions []MongoDbVersionsType
}

type MongoDbVersionsType struct {
	Name string
}

type Group struct {
	ID          string         `json:"id"`
	Name        string         `json:"name"`
	AgentAPIKey string         `json:"agentApiKey"`
	HostCounts  map[string]int `json:"hostCounts"`
}

type GroupCreateRequest struct {
	Name  string   `json:"name"`
	OrgId string   `json:"orgId,omitempty"`
	Tags  []string `json:"tags"`
}

type GroupUpdateRequest struct {
	Tags []string `json:"tags"`
}

type GroupHosts struct {
	TotalCount int `json:"totalCount"`
}

type DocContext struct {
	ID                   string
	Key                  string
	AdminPassword        string
	Version              string
	CompatibilityVersion string
	Nodes                []string
	Cluster              *Cluster
	Password             string
	RequireSSL           bool
}

type Cluster struct {
	Routers       []string
	ConfigServers []string
	Shards        [][]string
}

func (oc *OMClient) LoadDoc(p string, ctx *DocContext) (string, error) {
	t, ok := plans[p]
	if !ok {
		return "", fmt.Errorf("plan %q not found", p)
	}

	if ctx.Password == "" {
		var err error
		ctx.Password, err = GenerateString(32)
		if err != nil {
			panic(err)
		}
	}

	if strings.HasPrefix(ctx.Version, "3.4") {
		ctx.CompatibilityVersion = "3.4"
	} else if strings.HasPrefix(ctx.Version, "3.6") {
		ctx.CompatibilityVersion = "3.6"
	}

	b := bytes.Buffer{}
	if err := t.Execute(&b, ctx); err != nil {
		return "", err
	}
	return b.String(), nil
}

func (oc *OMClient) CreateGroup(id string, request GroupCreateRequest) (Group, error) {
	var group Group

	if request.Name == "" {
		request.Name = fmt.Sprintf("PCF_%s", id)
	}
	req, err := json.Marshal(request)
	if err != nil {
		return group, err
	}
	b, err := oc.doRequest("POST", "/api/public/v1.0/groups", bytes.NewReader(req))
	if err != nil {
		return group, err
	}

	if err = json.Unmarshal(b, &group); err != nil {
		return group, err
	}
	return group, nil
}

func (oc *OMClient) UpdateGroup(id string, request GroupUpdateRequest) (Group, error) {
	var group Group

	req, err := json.Marshal(request)
	if err != nil {
		return group, err
	}
	b, err := oc.doRequest("PATCH", fmt.Sprintf("/api/public/v1.0/groups/%s", id), bytes.NewReader(req))
	if err != nil {
		return group, err
	}

	if err = json.Unmarshal(b, &group); err != nil {
		return group, err
	}
	return group, nil
}

func (oc *OMClient) GetGroup(groupID string) (Group, error) {
	var group Group

	b, err := oc.doRequest("GET", fmt.Sprintf("/api/public/v1.0/groups/%s", groupID), nil)
	if err != nil {
		return group, err
	}

	if err = json.Unmarshal(b, &group); err != nil {
		return group, err
	}
	return group, nil
}

func (oc *OMClient) DeleteGroup(groupID string) error {
	_, err := oc.doRequest("DELETE", fmt.Sprintf("/api/public/v1.0/groups/%s", groupID), nil)
	return err
}

func (oc *OMClient) GetGroupHosts(groupID string) (GroupHosts, error) {
	var groupHosts GroupHosts

	b, err := oc.doRequest("GET", fmt.Sprintf("/api/public/v1.0/groups/%s/hosts", groupID), nil)
	if err != nil {
		return groupHosts, err
	}

	if err = json.Unmarshal(b, &groupHosts); err != nil {
		return groupHosts, err
	}
	return groupHosts, nil
}

func (oc *OMClient) GetGroupHostnames(groupID string, planID string) ([]string, error) {
	b, err := oc.doRequest("GET", fmt.Sprintf("/api/public/v1.0/groups/%s/hosts", groupID), nil)
	if err != nil {
		return nil, err
	}

	groupHostnames := gjson.GetBytes(b, fmt.Sprintf(`results.#.hostname`))
	if planID == "sharded_cluster" {
		groupHostnames = gjson.GetBytes(b, fmt.Sprintf(`results.#[typeName="SHARD_MONGOS"]#.hostname`))
	}

	servers := make([]string, len(groupHostnames.Array()))
	for i, node := range groupHostnames.Array() {
		servers[i] = fmt.Sprintf("%s:28000", node)
	}

	return servers, nil
}

func (oc *OMClient) ConfigureGroup(configurationDoc string, groupID string) error {
	u := fmt.Sprintf("/api/public/v1.0/groups/%s/automationConfig", groupID)
	b, err := oc.doRequest("PUT", u, strings.NewReader(configurationDoc))
	if err != nil {
		return err
	}
	log.Println(string(b))

	return nil
}

func (oc *OMClient) ConfigureMonitoringAgent(configurationDoc string, groupID string) error {
	u := fmt.Sprintf("/api/public/v1.0/groups/%s/automationConfig/monitoringAgentConfig", groupID)
	b, err := oc.doRequest("PUT", u, strings.NewReader(configurationDoc))
	if err != nil {
		return err
	}
	log.Println(string(b))

	return nil
}

func (oc *OMClient) ConfigureBackupAgent(configurationDoc string, groupID string) error {
	u := fmt.Sprintf("/api/public/v1.0/groups/%s/automationConfig/backupAgentConfig", groupID)
	b, err := oc.doRequest("PUT", u, strings.NewReader(configurationDoc))
	if err != nil {
		return err
	}
	log.Println(string(b))

	return nil
}
func (oc *OMClient) GetAvailableVersions(groupID string) (Automation, error) {
	var versions Automation

	b, err := oc.doRequest("GET", fmt.Sprintf("/api/public/v1.0/groups/%s/automationConfig", groupID), nil)
	if err != nil {
		return versions, err
	}

	if err = json.Unmarshal(b, &versions); err != nil {
		return versions, err
	}
	return versions, nil
}

func (oc *OMClient) GetLatestVersion(groupID string) string {
	cfg, err := oc.GetAvailableVersions(groupID)
	if err != nil {
		panic(err)
	}

	b := cfg.MongoDbVersions[len(cfg.MongoDbVersions)-1].Name

	return b
}

func (oc *OMClient) ValidateVersion(groupID string, version string) (string, error) {
	b, err := oc.doRequest("GET", fmt.Sprintf("/api/public/v1.0/groups/%s/automationConfig", groupID), nil)
	if err != nil {
		return "", err
	}

	v := gjson.GetBytes(b, fmt.Sprintf(`mongoDbVersions.#[name="%s"].name`, version))
	log.Printf("Using %q version of MongoDB", v.String())
	if v.String() == "" {
		log.Fatalf("failed to find expected version, got %s", version)
	}

	return v.String(), nil
}

func (oc *OMClient) doRequest(method string, path string, body io.Reader) ([]byte, error) {
	uri := fmt.Sprintf("%s%s", oc.Url, path)
	req, err := http.NewRequest(method, uri, body)
	if err != nil {
		return nil, err
	}

	req.Header.Set("Content-Type", "application/json")
	if err = digest.ApplyDigestAuth(oc.Username, oc.ApiKey, uri, req); err != nil {
		return nil, err
	}

	dump, err := httputil.DumpRequestOut(req, true)
	if err != nil {
		return nil, err
	}
	log.Printf("API Request: %q", dump)

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		log.Fatalf("%s %s error: %v", method, uri, err)
		return nil, err
	}
	defer res.Body.Close()

	dump, err = httputil.DumpResponse(res, true)
	if err != nil {
		return nil, err
	}
	log.Printf("API Response: %q", dump)

	b, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return nil, err
	}

	if res.StatusCode < 200 || res.StatusCode >= 300 {
		return nil, fmt.Errorf("%s %s request error: code=%d body=%q", method, path, res.StatusCode, b)
	}
	return b, nil
}
<commit_msg>Add compatibility version for mongo 4.0<commit_after>package adapter

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/httputil"
	"strings"

	"github.com/cf-platform-eng/mongodb-on-demand-release/src/mongodb-service-adapter/digest"
	"github.com/tidwall/gjson"
)

type OMClient struct {
	Url      string
	Username string
	ApiKey   string
}

type Automation struct {
	MongoDbVersions []MongoDbVersionsType
}

type MongoDbVersionsType struct {
	Name string
}

type Group struct {
	ID          string         `json:"id"`
	Name        string         `json:"name"`
	AgentAPIKey string         `json:"agentApiKey"`
	HostCounts  map[string]int `json:"hostCounts"`
}

type GroupCreateRequest struct {
	Name  string   `json:"name"`
	OrgId string   `json:"orgId,omitempty"`
	Tags  []string `json:"tags"`
}

type GroupUpdateRequest struct {
	Tags []string `json:"tags"`
}

type GroupHosts struct {
	TotalCount int `json:"totalCount"`
}

type DocContext struct {
	ID                   string
	Key                  string
	AdminPassword        string
	Version              string
	CompatibilityVersion string
	Nodes                []string
	Cluster              *Cluster
	Password             string
	RequireSSL           bool
}

type Cluster struct {
	Routers       []string
	ConfigServers []string
	Shards        [][]string
}

func (oc *OMClient) LoadDoc(p string, ctx *DocContext) (string, error) {
	t, ok := plans[p]
	if !ok {
		return "", fmt.Errorf("plan %q not found", p)
	}

	if ctx.Password == "" {
		var err error
		ctx.Password, err = GenerateString(32)
		if err != nil {
			panic(err)
		}
	}

	if strings.HasPrefix(ctx.Version, "3.4") {
		ctx.CompatibilityVersion = "3.4"
	} else if strings.HasPrefix(ctx.Version, "3.6") {
		ctx.CompatibilityVersion = "3.6"
	} else if strings.HasPrefix(ctx.Version, "4.0") {
		ctx.CompatibilityVersion = "4.0"
	}

	b := bytes.Buffer{}
	if err := t.Execute(&b, ctx); err != nil {
		return "", err
	}
	return b.String(), nil
}

func (oc *OMClient) CreateGroup(id string, request GroupCreateRequest) (Group, error) {
	var group Group

	if request.Name == "" {
		request.Name = fmt.Sprintf("PCF_%s", id)
	}
	req, err := json.Marshal(request)
	if err != nil {
		return group, err
	}
	b, err := oc.doRequest("POST", "/api/public/v1.0/groups", bytes.NewReader(req))
	if err != nil {
		return group, err
	}

	if err = json.Unmarshal(b, &group); err != nil {
		return group, err
	}
	return group, nil
}

func (oc *OMClient) UpdateGroup(id string, request GroupUpdateRequest) (Group, error) {
	var group Group

	req, err := json.Marshal(request)
	if err != nil {
		return group, err
	}
	b, err := oc.doRequest("PATCH", fmt.Sprintf("/api/public/v1.0/groups/%s", id), bytes.NewReader(req))
	if err != nil {
		return group, err
	}

	if err = json.Unmarshal(b, &group); err != nil {
		return group, err
	}
	return group, nil
}

func (oc *OMClient) GetGroup(groupID string) (Group, error) {
	var group Group

	b, err := oc.doRequest("GET", fmt.Sprintf("/api/public/v1.0/groups/%s", groupID), nil)
	if err != nil {
		return group, err
	}

	if err = json.Unmarshal(b, &group); err != nil {
		return group, err
	}
	return group, nil
}

func (oc *OMClient) DeleteGroup(groupID string) error {
	_, err := oc.doRequest("DELETE", fmt.Sprintf("/api/public/v1.0/groups/%s", groupID), nil)
	return err
}

func (oc *OMClient) GetGroupHosts(groupID string) (GroupHosts, error) {
	var groupHosts GroupHosts

	b, err := oc.doRequest("GET", fmt.Sprintf("/api/public/v1.0/groups/%s/hosts", groupID), nil)
	if err != nil {
		return groupHosts, err
	}

	if err = json.Unmarshal(b, &groupHosts); err != nil {
		return groupHosts, err
	}
	return groupHosts, nil
}

func (oc *OMClient) GetGroupHostnames(groupID string, planID string) ([]string, error) {
	b, err := oc.doRequest("GET", fmt.Sprintf("/api/public/v1.0/groups/%s/hosts", groupID), nil)
	if err != nil {
		return nil, err
	}

	groupHostnames := gjson.GetBytes(b, fmt.Sprintf(`results.#.hostname`))
	if planID == "sharded_cluster" {
		groupHostnames = gjson.GetBytes(b, fmt.Sprintf(`results.#[typeName="SHARD_MONGOS"]#.hostname`))
	}

	servers := make([]string, len(groupHostnames.Array()))
	for i, node := range groupHostnames.Array() {
		servers[i] = fmt.Sprintf("%s:28000", node)
	}

	return servers, nil
}

func (oc *OMClient) ConfigureGroup(configurationDoc string, groupID string) error {
	u := fmt.Sprintf("/api/public/v1.0/groups/%s/automationConfig", groupID)
	b, err := oc.doRequest("PUT", u, strings.NewReader(configurationDoc))
	if err != nil {
		return err
	}
	log.Println(string(b))

	return nil
}

func (oc *OMClient) ConfigureMonitoringAgent(configurationDoc string, groupID string) error {
	u := fmt.Sprintf("/api/public/v1.0/groups/%s/automationConfig/monitoringAgentConfig", groupID)
	b, err := oc.doRequest("PUT", u, strings.NewReader(configurationDoc))
	if err != nil {
		return err
	}
	log.Println(string(b))

	return nil
}

func (oc *OMClient) ConfigureBackupAgent(configurationDoc string, groupID string) error {
	u := fmt.Sprintf("/api/public/v1.0/groups/%s/automationConfig/backupAgentConfig", groupID)
	b, err := oc.doRequest("PUT", u, strings.NewReader(configurationDoc))
	if err != nil {
		return err
	}
	log.Println(string(b))

	return nil
}
func (oc *OMClient) GetAvailableVersions(groupID string) (Automation, error) {
	var versions Automation

	b, err := oc.doRequest("GET", fmt.Sprintf("/api/public/v1.0/groups/%s/automationConfig", groupID), nil)
	if err != nil {
		return versions, err
	}

	if err = json.Unmarshal(b, &versions); err != nil {
		return versions, err
	}
	return versions, nil
}

func (oc *OMClient) GetLatestVersion(groupID string) string {
	cfg, err := oc.GetAvailableVersions(groupID)
	if err != nil {
		panic(err)
	}

	b := cfg.MongoDbVersions[len(cfg.MongoDbVersions)-1].Name

	return b
}

func (oc *OMClient) ValidateVersion(groupID string, version string) (string, error) {
	b, err := oc.doRequest("GET", fmt.Sprintf("/api/public/v1.0/groups/%s/automationConfig", groupID), nil)
	if err != nil {
		return "", err
	}

	v := gjson.GetBytes(b, fmt.Sprintf(`mongoDbVersions.#[name="%s"].name`, version))
	log.Printf("Using %q version of MongoDB", v.String())
	if v.String() == "" {
		log.Fatalf("failed to find expected version, got %s", version)
	}

	return v.String(), nil
}

func (oc *OMClient) doRequest(method string, path string, body io.Reader) ([]byte, error) {
	uri := fmt.Sprintf("%s%s", oc.Url, path)
	req, err := http.NewRequest(method, uri, body)
	if err != nil {
		return nil, err
	}

	req.Header.Set("Content-Type", "application/json")
	if err = digest.ApplyDigestAuth(oc.Username, oc.ApiKey, uri, req); err != nil {
		return nil, err
	}

	dump, err := httputil.DumpRequestOut(req, true)
	if err != nil {
		return nil, err
	}
	log.Printf("API Request: %q", dump)

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		log.Fatalf("%s %s error: %v", method, uri, err)
		return nil, err
	}
	defer res.Body.Close()

	dump, err = httputil.DumpResponse(res, true)
	if err != nil {
		return nil, err
	}
	log.Printf("API Response: %q", dump)

	b, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return nil, err
	}

	if res.StatusCode < 200 || res.StatusCode >= 300 {
		return nil, fmt.Errorf("%s %s request error: code=%d body=%q", method, path, res.StatusCode, b)
	}
	return b, nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"html/template"
	"net/http"

	"github.com/gorilla/sessions"
	"github.com/jinzhu/gorm"
)

type Context struct {
	GlobalContext
	Session    *sessions.Session
	AdminLogin bool
	Captcha    *Deck
}

func NewContext(req *http.Request, gctx *GlobalContext) (*Context, error) {
	sess, err := gctx.Store.Get(req, "rtfblog")
	ctx := &Context{
		GlobalContext: *gctx,
		Session:       sess,
		AdminLogin:    sess.Values["adminlogin"] == "yes",
		Captcha:       deck,
	}
	return ctx, err
}

func MkFlashes(ctx *Context) template.HTML {
	flashes := ctx.Session.Flashes()
	html := ""
	// TODO: extract that to separate flashes template
	format := `

%s

`
	for _, f := range flashes {
		html = html + fmt.Sprintf(format, f)
	}
	return template.HTML(html)
}

func MkBasicData(ctx *Context, pageNo, offset int) TmplMap {
	numTotalPosts, err := ctx.Db.numPosts(ctx.AdminLogin)
	logger.LogIf(err)
	titles, err := ctx.Db.titles(NumRecentPosts, ctx.AdminLogin)
	logger.LogIf(err)
	posts, err := ctx.Db.posts(PostsPerPage, offset, ctx.AdminLogin)
	logger.LogIf(err)
	return TmplMap{
		"PageTitle":       L10n("Welcome"),
		"BlogTitle":       conf.Get("blog_title"),
		"BlogSubtitle":    conf.Get("blog_descr"),
		"NeedPagination":  numTotalPosts > PostsPerPage,
		"ListOfPages":     listOfPages(numTotalPosts, pageNo),
		"entries":         posts,
		"sidebar_entries": titles,
		"AdminLogin":      ctx.AdminLogin,
		"Version":         versionString(),
		"Flashes":         MkFlashes(ctx),
	}
}

func withTransaction(db Data, fn func(db Data) error) error {
	txErr := db.begin()
	if txErr != nil {
		return txErr
	}
	err := fn(db)
	if err != nil {
		db.rollback()
		return err
	}
	db.commit()
	return nil
}

func PublishCommentAndCommenter(db Data, postID int64, commenter *Commenter, rawBody string) (string, error) {
	var commentID int64
	err := withTransaction(db, func(db Data) error {
		commenterID, err := db.insertCommenter(commenter)
		if err != nil {
			return logger.LogIff(err, "db.insertCommenter() failed")
		}
		commentID, err = db.insertComment(commenterID, postID, rawBody)
		if err != nil {
			return logger.LogIff(err, "db.insertComment() failed")
		}
		return nil
	})
	return fmt.Sprintf("#comment-%d", commentID), err
}

func PublishComment(db Data, postID, commenterID int64, body string) (string, error) {
	var commentID int64
	err := withTransaction(db, func(db Data) error {
		var insErr error
		commentID, insErr = db.insertComment(commenterID, postID, body)
		return logger.LogIff(insErr, "db.insertComment() failed")
	})
	return fmt.Sprintf("#comment-%d", commentID), err
}

func InsertOrUpdatePost(db Data, post *EntryTable) (id int64, err error) {
	postID, idErr := db.postID(post.URL)
	if idErr != nil {
		if idErr == gorm.RecordNotFound {
			author, err := db.author()
			if err != nil {
				return -1, err
			}
			post.AuthorID = author.Id
			newPostID, err := db.insertPost(post)
			if err != nil {
				return -1, err
			}
			postID = newPostID
		} else {
			return -1, logger.LogIff(idErr, "db.postID() failed")
		}
	} else {
		post.Id = postID
		updErr := db.updatePost(post)
		if updErr != nil {
			return -1, updErr
		}
	}
	return postID, nil
}

func InsertOrUpdateAuthor(db Data, newAuthor *Author) (id int64, err error) {
	author, err := db.author() // Pick default author
	id = author.Id
	if err != nil {
		if err == gorm.RecordNotFound {
			id, err = db.insertAuthor(newAuthor)
		}
	} else {
		newAuthor.Id = id
		err = db.updateAuthor(newAuthor)
	}
	return id, logger.LogIff(err, "Failed to insert author")
}
<commit_msg>Don't fail to create context, log and continue<commit_after>package main

import (
	"fmt"
	"html/template"
	"net/http"

	"github.com/gorilla/sessions"
	"github.com/jinzhu/gorm"
)

type Context struct {
	GlobalContext
	Session    *sessions.Session
	AdminLogin bool
	Captcha    *Deck
}

func NewContext(req *http.Request, gctx *GlobalContext) (*Context, error) {
	sess, err := gctx.Store.Get(req, "rtfblog")
	logger.LogIf(err)
	ctx := &Context{
		GlobalContext: *gctx,
		Session:       sess,
		AdminLogin:    sess.Values["adminlogin"] == "yes",
		Captcha:       deck,
	}
	return ctx, nil
}

func MkFlashes(ctx *Context) template.HTML {
	flashes := ctx.Session.Flashes()
	html := ""
	// TODO: extract that to separate flashes template
	format := `

%s

`
	for _, f := range flashes {
		html = html + fmt.Sprintf(format, f)
	}
	return template.HTML(html)
}

func MkBasicData(ctx *Context, pageNo, offset int) TmplMap {
	numTotalPosts, err := ctx.Db.numPosts(ctx.AdminLogin)
	logger.LogIf(err)
	titles, err := ctx.Db.titles(NumRecentPosts, ctx.AdminLogin)
	logger.LogIf(err)
	posts, err := ctx.Db.posts(PostsPerPage, offset, ctx.AdminLogin)
	logger.LogIf(err)
	return TmplMap{
		"PageTitle":       L10n("Welcome"),
		"BlogTitle":       conf.Get("blog_title"),
		"BlogSubtitle":    conf.Get("blog_descr"),
		"NeedPagination":  numTotalPosts > PostsPerPage,
		"ListOfPages":     listOfPages(numTotalPosts, pageNo),
		"entries":         posts,
		"sidebar_entries": titles,
		"AdminLogin":      ctx.AdminLogin,
		"Version":         versionString(),
		"Flashes":         MkFlashes(ctx),
	}
}

func withTransaction(db Data, fn func(db Data) error) error {
	txErr := db.begin()
	if txErr != nil {
		return txErr
	}
	err := fn(db)
	if err != nil {
		db.rollback()
		return err
	}
	db.commit()
	return nil
}

func PublishCommentAndCommenter(db Data, postID int64, commenter *Commenter, rawBody string) (string, error) {
	var commentID int64
	err := withTransaction(db, func(db Data) error {
		commenterID, err := db.insertCommenter(commenter)
		if err != nil {
			return logger.LogIff(err, "db.insertCommenter() failed")
		}
		commentID, err = db.insertComment(commenterID, postID, rawBody)
		if err != nil {
			return logger.LogIff(err, "db.insertComment() failed")
		}
		return nil
	})
	return fmt.Sprintf("#comment-%d", commentID), err
}

func PublishComment(db Data, postID, commenterID int64, body string) (string, error) {
	var commentID int64
	err := withTransaction(db, func(db Data) error {
		var insErr error
		commentID, insErr = db.insertComment(commenterID, postID, body)
		return logger.LogIff(insErr, "db.insertComment() failed")
	})
	return fmt.Sprintf("#comment-%d", commentID), err
}

func InsertOrUpdatePost(db Data, post *EntryTable) (id int64, err error) {
	postID, idErr := db.postID(post.URL)
	if idErr != nil {
		if idErr == gorm.RecordNotFound {
			author, err := db.author()
			if err != nil {
				return -1, err
			}
			post.AuthorID = author.Id
			newPostID, err := db.insertPost(post)
			if err != nil {
				return -1, err
			}
			postID = newPostID
		} else {
			return -1, logger.LogIff(idErr, "db.postID() failed")
		}
	} else {
		post.Id = postID
		updErr := db.updatePost(post)
		if updErr != nil {
			return -1, updErr
		}
	}
	return postID, nil
}

func InsertOrUpdateAuthor(db Data, newAuthor *Author) (id int64, err error) {
	author, err := db.author() // Pick default author
	id = author.Id
	if err != nil {
		if err == gorm.RecordNotFound {
			id, err = db.insertAuthor(newAuthor)
		}
	} else {
		newAuthor.Id = id
		err = db.updateAuthor(newAuthor)
	}
	return id, logger.LogIff(err, "Failed to insert author")
}
<|endoftext|>


<commit_before>package osc

import (
	"fmt"
	"log"
	"net"
	"strings"
	"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/resource"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceAwsEip() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsEipCreate,
		Read:   resourceAwsEipRead,
		Update: resourceAwsEipUpdate,
		Delete: resourceAwsEipDelete,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Schema: map[string]*schema.Schema{
			"vpc": &schema.Schema{
				Type:     schema.TypeBool,
				Optional: true,
				ForceNew: true,
				Computed: true,
			},

			"instance": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},

			"network_interface": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},

			"allocation_id": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"association_id": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"domain": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"public_ip": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"private_ip": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"associate_with_private_ip": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
			},
		},
	}
}

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

	// By default, we're not in a VPC
	domainOpt := ""
	if v := d.Get("vpc"); v != nil && v.(bool) {
		domainOpt = "vpc"
	}

	allocOpts := &ec2.AllocateAddressInput{
		Domain: aws.String(domainOpt),
	}

	log.Printf("[DEBUG] EIP create configuration: %#v", allocOpts)
	allocResp, err := ec2conn.AllocateAddress(allocOpts)
	if err != nil {
		return fmt.Errorf("Error creating EIP: %s", err)
	}

	// The domain tells us if we're in a VPC or not
	d.Set("domain", allocResp.Domain)

	// Assign the eips (unique) allocation id for use later
	// the EIP api has a conditional unique ID (really), so
	// if we're in a VPC we need to save the ID as such, otherwise
	// it defaults to using the public IP
	log.Printf("[DEBUG] EIP Allocate: %#v", allocResp)
	if d.Get("domain").(string) == "vpc" {
		d.SetId(*allocResp.AllocationId)
	} else {
		d.SetId(*allocResp.PublicIp)
	}

	log.Printf("[INFO] EIP ID: %s (domain: %v)", d.Id(), *allocResp.Domain)
	return resourceAwsEipUpdate(d, meta)
}

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

	domain := resourceAwsEipDomain(d)
	id := d.Id()

	req := &ec2.DescribeAddressesInput{}

	if domain == "vpc" {
		req.AllocationIds = []*string{aws.String(id)}
	} else {
		req.PublicIps = []*string{aws.String(id)}
	}

	log.Printf(
		"[DEBUG] EIP describe configuration: %s (domain: %s)",
		req, domain)

	describeAddresses, err := ec2conn.DescribeAddresses(req)
	if err != nil {
		if ec2err, ok := err.(awserr.Error); ok && (ec2err.Code() == "InvalidAllocationID.NotFound" || ec2err.Code() == "InvalidAddress.NotFound") {
			d.SetId("")
			return nil
		}

		return fmt.Errorf("Error retrieving EIP: %s", err)
	}

	// Verify AWS returned our EIP
	if len(describeAddresses.Addresses) != 1 ||
		domain == "vpc" && *describeAddresses.Addresses[0].AllocationId != id ||
		*describeAddresses.Addresses[0].PublicIp != id {
		if err != nil {
			return fmt.Errorf("Unable to find EIP: %#v", describeAddresses.Addresses)
		}
	}

	address := describeAddresses.Addresses[0]

	d.Set("association_id", address.AssociationId)
	if address.InstanceId != nil {
		d.Set("instance", address.InstanceId)
	} else {
		d.Set("instance", "")
	}
	if address.NetworkInterfaceId != nil {
		d.Set("network_interface", address.NetworkInterfaceId)
	} else {
		d.Set("network_interface", "")
	}
	d.Set("private_ip", address.PrivateIpAddress)
	d.Set("public_ip", address.PublicIp)

	// On import (domain never set, which it must've been if we created),
	// set the 'vpc' attribute depending on if we're in a VPC.
	if address.Domain != nil {
		d.Set("vpc", *address.Domain == "vpc")
	}

	d.Set("domain", address.Domain)

	// Force ID to be an Allocation ID if we're on a VPC
	// This allows users to import the EIP based on the IP if they are in a VPC
	if *address.Domain == "vpc" && net.ParseIP(id) != nil {
		log.Printf("[DEBUG] Re-assigning EIP ID (%s) to it's Allocation ID (%s)", d.Id(), *address.AllocationId)
		d.SetId(*address.AllocationId)
	}

	return nil
}

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

	domain := resourceAwsEipDomain(d)

	// Associate to instance or interface if specified
	v_instance, ok_instance := d.GetOk("instance")
	v_interface, ok_interface := d.GetOk("network_interface")

	if ok_instance || ok_interface {
		instanceId := v_instance.(string)
		networkInterfaceId := v_interface.(string)

		assocOpts := &ec2.AssociateAddressInput{
			InstanceId: aws.String(instanceId),
			PublicIp:   aws.String(d.Id()),
		}

		// more unique ID conditionals
		if domain == "vpc" {
			var privateIpAddress *string
			if v := d.Get("associate_with_private_ip").(string); v != "" {
				privateIpAddress = aws.String(v)
			}
			assocOpts = &ec2.AssociateAddressInput{
				NetworkInterfaceId: aws.String(networkInterfaceId),
				InstanceId:         aws.String(instanceId),
				AllocationId:       aws.String(d.Id()),
				PrivateIpAddress:   privateIpAddress,
			}
		}

		log.Printf("[DEBUG] EIP associate configuration: %s (domain: %s)", assocOpts, domain)

		err := resource.Retry(1*time.Minute, func() *resource.RetryError {
			_, err := ec2conn.AssociateAddress(assocOpts)
			if err != nil {
				if awsErr, ok := err.(awserr.Error); ok {
					if awsErr.Code() == "InvalidAllocationID.NotFound" {
						return resource.RetryableError(awsErr)
					}
				}
				return resource.NonRetryableError(err)
			}
			return nil
		})
		if err != nil {
			// Prevent saving instance if association failed
			// e.g. missing internet gateway in VPC
			d.Set("instance", "")
			d.Set("network_interface", "")
			return fmt.Errorf("Failure associating EIP: %s", err)
		}
	}

	return resourceAwsEipRead(d, meta)
}

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

	if err := resourceAwsEipRead(d, meta); err != nil {
		return err
	}
	if d.Id() == "" {
		// This might happen from the read
		return nil
	}

	// If we are attached to an instance or interface, detach first.
	if d.Get("instance").(string) != "" || d.Get("association_id").(string) != "" {
		log.Printf("[DEBUG] Disassociating EIP: %s", d.Id())
		var err error
		switch resourceAwsEipDomain(d) {
		case "vpc":
			_, err = ec2conn.DisassociateAddress(&ec2.DisassociateAddressInput{
				AssociationId: aws.String(d.Get("association_id").(string)),
			})
		case "standard":
			_, err = ec2conn.DisassociateAddress(&ec2.DisassociateAddressInput{
				PublicIp: aws.String(d.Get("public_ip").(string)),
			})
		}

		if err != nil {
			// First check if the association ID is not found. If this
			// is the case, then it was already disassociated somehow,
			// and that is okay. The most commmon reason for this is that
			// the instance or ENI it was attached it was destroyed.
			if ec2err, ok := err.(awserr.Error); ok && ec2err.Code() == "InvalidAssociationID.NotFound" {
				err = nil
			}
		}

		if err != nil {
			return err
		}
	}

	domain := resourceAwsEipDomain(d)
	return resource.Retry(3*time.Minute, func() *resource.RetryError {
		var err error
		switch domain {
		case "vpc":
			log.Printf(
				"[DEBUG] EIP release (destroy) address allocation: %v",
				d.Id())
			_, err = ec2conn.ReleaseAddress(&ec2.ReleaseAddressInput{
				AllocationId: aws.String(d.Id()),
			})
		case "standard":
			log.Printf("[DEBUG] EIP release (destroy) address: %v", d.Id())
			_, err = ec2conn.ReleaseAddress(&ec2.ReleaseAddressInput{
				PublicIp: aws.String(d.Id()),
			})
		}

		if err == nil {
			return nil
		}
		if _, ok := err.(awserr.Error); !ok {
			return resource.NonRetryableError(err)
		}

		return resource.RetryableError(err)
	})
}

func resourceAwsEipDomain(d *schema.ResourceData) string {
	if v, ok := d.GetOk("domain"); ok {
		return v.(string)
	} else if strings.Contains(d.Id(), "eipalloc") {
		// We have to do this for backwards compatibility since TF 0.1
		// didn't have the "domain" computed attribute.
		return "vpc"
	}

	return "standard"
}
<commit_msg>fix eip domain<commit_after>package osc

import (
	"fmt"
	"log"
	"net"
	"strings"
	"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/resource"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceAwsEip() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsEipCreate,
		Read:   resourceAwsEipRead,
		Update: resourceAwsEipUpdate,
		Delete: resourceAwsEipDelete,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Schema: map[string]*schema.Schema{
			"vpc": &schema.Schema{
				Type:     schema.TypeBool,
				Optional: true,
				ForceNew: true,
				Computed: true,
			},

			"instance": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},

			"network_interface": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},

			"allocation_id": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"association_id": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"domain": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"public_ip": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"private_ip": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"associate_with_private_ip": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
			},
		},
	}
}

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

	// By default, we're not in a VPC
	domainOpt := "standard"
	if v := d.Get("vpc"); v != nil && v.(bool) {
		domainOpt = "vpc"
	}

	allocOpts := &ec2.AllocateAddressInput{
		Domain: aws.String(domainOpt),
	}

	log.Printf("[DEBUG] EIP create configuration: %#v", allocOpts)
	allocResp, err := ec2conn.AllocateAddress(allocOpts)
	if err != nil {
		return fmt.Errorf("Error creating EIP: %s", err)
	}

	// The domain tells us if we're in a VPC or not
	d.Set("domain", allocResp.Domain)

	// Assign the eips (unique) allocation id for use later
	// the EIP api has a conditional unique ID (really), so
	// if we're in a VPC we need to save the ID as such, otherwise
	// it defaults to using the public IP
	log.Printf("[DEBUG] EIP Allocate: %#v", allocResp)
	if d.Get("domain").(string) == "vpc" {
		d.SetId(*allocResp.AllocationId)
	} else {
		d.SetId(*allocResp.PublicIp)
	}

	log.Printf("[INFO] EIP ID: %s (domain: %v)", d.Id(), *allocResp.Domain)
	return resourceAwsEipUpdate(d, meta)
}

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

	domain := resourceAwsEipDomain(d)
	id := d.Id()

	req := &ec2.DescribeAddressesInput{}

	if domain == "vpc" {
		req.AllocationIds = []*string{aws.String(id)}
	} else {
		req.PublicIps = []*string{aws.String(id)}
	}

	log.Printf(
		"[DEBUG] EIP describe configuration: %s (domain: %s)",
		req, domain)

	describeAddresses, err := ec2conn.DescribeAddresses(req)
	if err != nil {
		if ec2err, ok := err.(awserr.Error); ok && (ec2err.Code() == "InvalidAllocationID.NotFound" || ec2err.Code() == "InvalidAddress.NotFound") {
			d.SetId("")
			return nil
		}

		return fmt.Errorf("Error retrieving EIP: %s", err)
	}

	// Verify AWS returned our EIP
	if len(describeAddresses.Addresses) != 1 ||
		domain == "vpc" && *describeAddresses.Addresses[0].AllocationId != id ||
		*describeAddresses.Addresses[0].PublicIp != id {
		if err != nil {
			return fmt.Errorf("Unable to find EIP: %#v", describeAddresses.Addresses)
		}
	}

	address := describeAddresses.Addresses[0]

	d.Set("association_id", address.AssociationId)
	if address.InstanceId != nil {
		d.Set("instance", address.InstanceId)
	} else {
		d.Set("instance", "")
	}
	if address.NetworkInterfaceId != nil {
		d.Set("network_interface", address.NetworkInterfaceId)
	} else {
		d.Set("network_interface", "")
	}
	d.Set("private_ip", address.PrivateIpAddress)
	d.Set("public_ip", address.PublicIp)

	// On import (domain never set, which it must've been if we created),
	// set the 'vpc' attribute depending on if we're in a VPC.
	if address.Domain != nil {
		d.Set("vpc", *address.Domain == "vpc")
	}

	d.Set("domain", address.Domain)

	// Force ID to be an Allocation ID if we're on a VPC
	// This allows users to import the EIP based on the IP if they are in a VPC
	if *address.Domain == "vpc" && net.ParseIP(id) != nil {
		log.Printf("[DEBUG] Re-assigning EIP ID (%s) to it's Allocation ID (%s)", d.Id(), *address.AllocationId)
		d.SetId(*address.AllocationId)
	}

	return nil
}

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

	domain := resourceAwsEipDomain(d)

	// Associate to instance or interface if specified
	v_instance, ok_instance := d.GetOk("instance")
	v_interface, ok_interface := d.GetOk("network_interface")

	if ok_instance || ok_interface {
		instanceId := v_instance.(string)
		networkInterfaceId := v_interface.(string)

		assocOpts := &ec2.AssociateAddressInput{
			InstanceId: aws.String(instanceId),
			PublicIp:   aws.String(d.Id()),
		}

		// more unique ID conditionals
		if domain == "vpc" {
			var privateIpAddress *string
			if v := d.Get("associate_with_private_ip").(string); v != "" {
				privateIpAddress = aws.String(v)
			}
			assocOpts = &ec2.AssociateAddressInput{
				NetworkInterfaceId: aws.String(networkInterfaceId),
				InstanceId:         aws.String(instanceId),
				AllocationId:       aws.String(d.Id()),
				PrivateIpAddress:   privateIpAddress,
			}
		}

		log.Printf("[DEBUG] EIP associate configuration: %s (domain: %s)", assocOpts, domain)

		err := resource.Retry(1*time.Minute, func() *resource.RetryError {
			_, err := ec2conn.AssociateAddress(assocOpts)
			if err != nil {
				if awsErr, ok := err.(awserr.Error); ok {
					if awsErr.Code() == "InvalidAllocationID.NotFound" {
						return resource.RetryableError(awsErr)
					}
				}
				return resource.NonRetryableError(err)
			}
			return nil
		})
		if err != nil {
			// Prevent saving instance if association failed
			// e.g. missing internet gateway in VPC
			d.Set("instance", "")
			d.Set("network_interface", "")
			return fmt.Errorf("Failure associating EIP: %s", err)
		}
	}

	return resourceAwsEipRead(d, meta)
}

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

	if err := resourceAwsEipRead(d, meta); err != nil {
		return err
	}
	if d.Id() == "" {
		// This might happen from the read
		return nil
	}

	// If we are attached to an instance or interface, detach first.
	if d.Get("instance").(string) != "" || d.Get("association_id").(string) != "" {
		log.Printf("[DEBUG] Disassociating EIP: %s", d.Id())
		var err error
		switch resourceAwsEipDomain(d) {
		case "vpc":
			_, err = ec2conn.DisassociateAddress(&ec2.DisassociateAddressInput{
				AssociationId: aws.String(d.Get("association_id").(string)),
			})
		case "standard":
			_, err = ec2conn.DisassociateAddress(&ec2.DisassociateAddressInput{
				PublicIp: aws.String(d.Get("public_ip").(string)),
			})
		}

		if err != nil {
			// First check if the association ID is not found. If this
			// is the case, then it was already disassociated somehow,
			// and that is okay. The most commmon reason for this is that
			// the instance or ENI it was attached it was destroyed.
			if ec2err, ok := err.(awserr.Error); ok && ec2err.Code() == "InvalidAssociationID.NotFound" {
				err = nil
			}
		}

		if err != nil {
			return err
		}
	}

	domain := resourceAwsEipDomain(d)
	return resource.Retry(3*time.Minute, func() *resource.RetryError {
		var err error
		switch domain {
		case "vpc":
			log.Printf(
				"[DEBUG] EIP release (destroy) address allocation: %v",
				d.Id())
			_, err = ec2conn.ReleaseAddress(&ec2.ReleaseAddressInput{
				AllocationId: aws.String(d.Id()),
			})
		case "standard":
			log.Printf("[DEBUG] EIP release (destroy) address: %v", d.Id())
			_, err = ec2conn.ReleaseAddress(&ec2.ReleaseAddressInput{
				PublicIp: aws.String(d.Id()),
			})
		}

		if err == nil {
			return nil
		}
		if _, ok := err.(awserr.Error); !ok {
			return resource.NonRetryableError(err)
		}

		return resource.RetryableError(err)
	})
}

func resourceAwsEipDomain(d *schema.ResourceData) string {
	if v, ok := d.GetOk("domain"); ok {
		return v.(string)
	} else if strings.Contains(d.Id(), "eipalloc") {
		// We have to do this for backwards compatibility since TF 0.1
		// didn't have the "domain" computed attribute.
		return "vpc"
	}

	return "standard"
}
<|endoftext|>


<commit_before>package data

import (
	"fmt"
	"gopkg.in/yaml.v1"
	"io/ioutil"
	"math/rand"
	"time"
)

func Load(name, locale, path string) string {
	fi, err := ioutil.ReadFile(fmt.Sprintf("locale/%s/%s.yml", path, locale))
	if err != nil {
		panic(err)
	}

	m := make(map[string]interface{})

	err = yaml.Unmarshal([]byte(fi), &m)
	if err != nil {
		panic(err)
	}

	stringValue, isString := m[name].(string)
	arrayValue, isArray := m[name].([]interface{})
	if isString {
		return stringValue
	} else if isArray {
		return randomElement(arrayValue)
	}

	return "OPS"
}

func randomElement(array []interface{}) string {
	rand.Seed(time.Now().UTC().UnixNano())
	a, _ := array[rand.Intn(len(array))].(string)
	return a
}
<commit_msg>better error message<commit_after>package data

import (
	"fmt"
	"gopkg.in/yaml.v1"
	"io/ioutil"
	"math/rand"
	"time"
)

func Load(name, locale, path string) string {
	fi, err := ioutil.ReadFile(fmt.Sprintf("locale/%s/%s.yml", path, locale))
	if err != nil {
		panic(err)
	}

	m := make(map[string]interface{})

	err = yaml.Unmarshal([]byte(fi), &m)
	if err != nil {
		panic(err)
	}

	stringValue, isString := m[name].(string)
	arrayValue, isArray := m[name].([]interface{})
	if isString {
		return stringValue
	} else if isArray {
		return randomElement(arrayValue)
	}

	return fmt.Sprintf("[Failed to load %v from %v-%v]", name, path, locale)
}

func randomElement(array []interface{}) string {
	rand.Seed(time.Now().UTC().UnixNano())
	a, _ := array[rand.Intn(len(array))].(string)
	return a
}
<|endoftext|>


<commit_before>package storage

import (
	proto "code.google.com/p/goprotobuf/proto"
	"code.google.com/p/weed-fs/go/glog"
	"code.google.com/p/weed-fs/go/operation"
	"code.google.com/p/weed-fs/go/util"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"math/rand"
	"strconv"
	"strings"
)

type DiskLocation struct {
	Directory      string
	MaxVolumeCount int
	volumes        map[VolumeId]*Volume
}

func (mn *DiskLocation) reset() {
}

type MasterNodes struct {
	nodes    []string
	lastNode int
}

func NewMasterNodes(bootstrapNode string) (mn *MasterNodes) {
	mn = &MasterNodes{nodes: []string{bootstrapNode}, lastNode: -1}
	return
}
func (mn *MasterNodes) reset() {
	if len(mn.nodes) > 1 && mn.lastNode > 0 {
		mn.lastNode = -mn.lastNode
	}
}
func (mn *MasterNodes) findMaster() (string, error) {
	if len(mn.nodes) == 0 {
		return "", errors.New("No master node found!")
	}
	if mn.lastNode < 0 {
		for _, m := range mn.nodes {
			if masters, e := operation.ListMasters(m); e == nil {
				if len(masters) == 0 {
					continue
				}
				mn.nodes = masters
				mn.lastNode = rand.Intn(len(mn.nodes))
				glog.V(2).Info("current master node is :", mn.nodes[mn.lastNode])
				break
			}
		}
	}
	if mn.lastNode < 0 {
		return "", errors.New("No master node avalable!")
	}
	return mn.nodes[mn.lastNode], nil
}

type Store struct {
	Port            int
	Ip              string
	PublicUrl       string
	Locations       []*DiskLocation
	dataCenter      string //optional informaton, overwriting master setting if exists
	rack            string //optional information, overwriting master setting if exists
	connected       bool
	volumeSizeLimit uint64 //read from the master
	masterNodes     *MasterNodes
}

func NewStore(port int, ip, publicUrl string, dirnames []string, maxVolumeCounts []int) (s *Store) {
	s = &Store{Port: port, Ip: ip, PublicUrl: publicUrl}
	s.Locations = make([]*DiskLocation, 0)
	for i := 0; i < len(dirnames); i++ {
		location := &DiskLocation{Directory: dirnames[i], MaxVolumeCount: maxVolumeCounts[i]}
		location.volumes = make(map[VolumeId]*Volume)
		location.loadExistingVolumes()
		s.Locations = append(s.Locations, location)
	}
	return
}
func (s *Store) AddVolume(volumeListString string, collection string, replicaPlacement string) error {
	rt, e := NewReplicaPlacementFromString(replicaPlacement)
	if e != nil {
		return e
	}
	for _, range_string := range strings.Split(volumeListString, ",") {
		if strings.Index(range_string, "-") < 0 {
			id_string := range_string
			id, err := NewVolumeId(id_string)
			if err != nil {
				return fmt.Errorf("Volume Id %s is not a valid unsigned integer!", id_string)
			}
			e = s.addVolume(VolumeId(id), collection, rt)
		} else {
			pair := strings.Split(range_string, "-")
			start, start_err := strconv.ParseUint(pair[0], 10, 64)
			if start_err != nil {
				return fmt.Errorf("Volume Start Id %s is not a valid unsigned integer!", pair[0])
			}
			end, end_err := strconv.ParseUint(pair[1], 10, 64)
			if end_err != nil {
				return fmt.Errorf("Volume End Id %s is not a valid unsigned integer!", pair[1])
			}
			for id := start; id <= end; id++ {
				if err := s.addVolume(VolumeId(id), collection, rt); err != nil {
					e = err
				}
			}
		}
	}
	return e
}
func (s *Store) DeleteCollection(collection string) (e error) {
	for _, location := range s.Locations {
		for k, v := range location.volumes {
			if v.Collection == collection {
				e = v.Destroy()
				if e != nil {
					return
				}
				delete(location.volumes, k)
			}
		}
	}
	return
}
func (s *Store) findVolume(vid VolumeId) *Volume {
	for _, location := range s.Locations {
		if v, found := location.volumes[vid]; found {
			return v
		}
	}
	return nil
}
func (s *Store) findFreeLocation() (ret *DiskLocation) {
	max := 0
	for _, location := range s.Locations {
		currentFreeCount := location.MaxVolumeCount - len(location.volumes)
		if currentFreeCount > max {
			max = currentFreeCount
			ret = location
		}
	}
	return ret
}
func (s *Store) addVolume(vid VolumeId, collection string, replicaPlacement *ReplicaPlacement) error {
	if s.findVolume(vid) != nil {
		return fmt.Errorf("Volume Id %d already exists!", vid)
	}
	if location := s.findFreeLocation(); location != nil {
		glog.V(0).Infoln("In dir", location.Directory, "adds volume =", vid, ", collection =", collection, ", replicaPlacement =", replicaPlacement)
		if volume, err := NewVolume(location.Directory, collection, vid, replicaPlacement); err == nil {
			location.volumes[vid] = volume
			return nil
		} else {
			return err
		}
	}
	return fmt.Errorf("No more free space left")
}

func (s *Store) CheckCompactVolume(volumeIdString string, garbageThresholdString string) (error, bool) {
	vid, err := NewVolumeId(volumeIdString)
	if err != nil {
		return fmt.Errorf("Volume Id %s is not a valid unsigned integer!", volumeIdString), false
	}
	garbageThreshold, e := strconv.ParseFloat(garbageThresholdString, 32)
	if e != nil {
		return fmt.Errorf("garbageThreshold %s is not a valid float number!", garbageThresholdString), false
	}
	if v := s.findVolume(vid); v != nil {
		glog.V(3).Infoln(vid, "garbage level is", v.garbageLevel())
		return nil, garbageThreshold < v.garbageLevel()
	}
	return fmt.Errorf("volume id %d is not found during check compact!", vid), false
}
func (s *Store) CompactVolume(volumeIdString string) error {
	vid, err := NewVolumeId(volumeIdString)
	if err != nil {
		return fmt.Errorf("Volume Id %s is not a valid unsigned integer!", volumeIdString)
	}
	if v := s.findVolume(vid); v != nil {
		return v.Compact()
	}
	return fmt.Errorf("volume id %d is not found during compact!", vid)
}
func (s *Store) CommitCompactVolume(volumeIdString string) error {
	vid, err := NewVolumeId(volumeIdString)
	if err != nil {
		return fmt.Errorf("Volume Id %s is not a valid unsigned integer!", volumeIdString)
	}
	if v := s.findVolume(vid); v != nil {
		return v.commitCompact()
	}
	return fmt.Errorf("volume id %d is not found during commit compact!", vid)
}
func (s *Store) FreezeVolume(volumeIdString string) error {
	vid, err := NewVolumeId(volumeIdString)
	if err != nil {
		return fmt.Errorf("Volume Id %s is not a valid unsigned integer!", volumeIdString)
	}
	if v := s.findVolume(vid); v != nil {
		if v.readOnly {
			return fmt.Errorf("Volume %s is already read-only", volumeIdString)
		}
		return v.freeze()
	}
	return fmt.Errorf("volume id %d is not found during freeze!", vid)
}
func (l *DiskLocation) loadExistingVolumes() {
	if dirs, err := ioutil.ReadDir(l.Directory); err == nil {
		for _, dir := range dirs {
			name := dir.Name()
			if !dir.IsDir() && strings.HasSuffix(name, ".dat") {
				collection := ""
				base := name[:len(name)-len(".dat")]
				i := strings.Index(base, "_")
				if i > 0 {
					collection, base = base[0:i], base[i+1:]
				}
				if vid, err := NewVolumeId(base); err == nil {
					if l.volumes[vid] == nil {
						if v, e := NewVolume(l.Directory, collection, vid, nil); e == nil {
							l.volumes[vid] = v
							glog.V(0).Infoln("data file", l.Directory+"/"+name, "replicaPlacement =", v.ReplicaPlacement, "version =", v.Version(), "size =", v.Size())
						}
					}
				}
			}
		}
	}
	glog.V(0).Infoln("Store started on dir:", l.Directory, "with", len(l.volumes), "volumes", "max", l.MaxVolumeCount)
}
func (s *Store) Status() []*VolumeInfo {
	var stats []*VolumeInfo
	for _, location := range s.Locations {
		for k, v := range location.volumes {
			s := &VolumeInfo{Id: VolumeId(k), Size: v.ContentSize(),
				Collection:       v.Collection,
				ReplicaPlacement: v.ReplicaPlacement,
				Version:          v.Version(),
				FileCount:        v.nm.FileCount(),
				DeleteCount:      v.nm.DeletedCount(),
				DeletedByteCount: v.nm.DeletedSize(),
				ReadOnly:         v.readOnly}
			stats = append(stats, s)
		}
	}
	return stats
}

func (s *Store) SetDataCenter(dataCenter string) {
	s.dataCenter = dataCenter
}
func (s *Store) SetRack(rack string) {
	s.rack = rack
}

func (s *Store) SetBootstrapMaster(bootstrapMaster string) {
	s.masterNodes = NewMasterNodes(bootstrapMaster)
}
func (s *Store) Join() (masterNode string, e error) {
	masterNode, e = s.masterNodes.findMaster()
	if e != nil {
		return
	}
	var volumeMessages []*operation.VolumeInformationMessage
	maxVolumeCount := 0
	var maxFileKey uint64
	for _, location := range s.Locations {
		maxVolumeCount = maxVolumeCount + location.MaxVolumeCount
		for k, v := range location.volumes {
			volumeMessage := &operation.VolumeInformationMessage{
				Id:               proto.Uint32(uint32(k)),
				Size:             proto.Uint64(uint64(v.Size())),
				Collection:       proto.String(v.Collection),
				FileCount:        proto.Uint64(uint64(v.nm.FileCount())),
				DeleteCount:      proto.Uint64(uint64(v.nm.DeletedCount())),
				DeletedByteCount: proto.Uint64(v.nm.DeletedSize()),
				ReadOnly:         proto.Bool(v.readOnly),
				ReplicaPlacement: proto.Uint32(uint32(v.ReplicaPlacement.Byte())),
				Version:          proto.Uint32(uint32(v.Version())),
			}
			volumeMessages = append(volumeMessages, volumeMessage)
			if maxFileKey < v.nm.MaxFileKey() {
				maxFileKey = v.nm.MaxFileKey()
			}
		}
	}

	joinMessage := &operation.JoinMessage{
		IsInit:         proto.Bool(!s.connected),
		Ip:             proto.String(s.Ip),
		Port:           proto.Uint32(uint32(s.Port)),
		PublicUrl:      proto.String(s.PublicUrl),
		MaxVolumeCount: proto.Uint32(uint32(maxVolumeCount)),
		MaxFileKey:     proto.Uint64(maxFileKey),
		DataCenter:     proto.String(s.dataCenter),
		Rack:           proto.String(s.rack),
		Volumes:        volumeMessages,
	}

	data, err := proto.Marshal(joinMessage)
	if err != nil {
		return "", err
	}

	println("join data size", len(data))

	jsonBlob, err := util.PostBytes("http://"+masterNode+"/dir/join", data)
	if err != nil {
		s.masterNodes.reset()
		return "", err
	}
	var ret operation.JoinResult
	if err := json.Unmarshal(jsonBlob, &ret); err != nil {
		return masterNode, err
	}
	if ret.Error != "" {
		return masterNode, errors.New(ret.Error)
	}
	s.volumeSizeLimit = ret.VolumeSizeLimit
	s.connected = true
	return
}
func (s *Store) Close() {
	for _, location := range s.Locations {
		for _, v := range location.volumes {
			v.Close()
		}
	}
}
func (s *Store) Write(i VolumeId, n *Needle) (size uint32, err error) {
	if v := s.findVolume(i); v != nil {
		if v.readOnly {
			err = fmt.Errorf("Volume %d is read only!", i)
			return
		} else {
			if MaxPossibleVolumeSize >= v.ContentSize()+uint64(size) {
				size, err = v.write(n)
			} else {
				err = fmt.Errorf("Volume Size Limit %d Exceeded! Current size is %d", s.volumeSizeLimit, v.ContentSize())
			}
			if s.volumeSizeLimit < v.ContentSize()+3*uint64(size) {
				glog.V(0).Infoln("volume", i, "size", v.ContentSize(), "will exceed limit", s.volumeSizeLimit)
				if _, e := s.Join(); e != nil {
					glog.V(0).Infoln("error when reporting size:", e)
				}
			}
		}
		return
	}
	glog.V(0).Infoln("volume", i, "not found!")
	err = fmt.Errorf("Volume %d not found!", i)
	return
}
func (s *Store) Delete(i VolumeId, n *Needle) (uint32, error) {
	if v := s.findVolume(i); v != nil && !v.readOnly {
		return v.delete(n)
	}
	return 0, nil
}
func (s *Store) Read(i VolumeId, n *Needle) (int, error) {
	if v := s.findVolume(i); v != nil {
		return v.read(n)
	}
	return 0, fmt.Errorf("Volume %v not found!", i)
}
func (s *Store) GetVolume(i VolumeId) *Volume {
	return s.findVolume(i)
}

func (s *Store) HasVolume(i VolumeId) bool {
	v := s.findVolume(i)
	return v != nil
}
<commit_msg>remove the println<commit_after>package storage

import (
	proto "code.google.com/p/goprotobuf/proto"
	"code.google.com/p/weed-fs/go/glog"
	"code.google.com/p/weed-fs/go/operation"
	"code.google.com/p/weed-fs/go/util"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"math/rand"
	"strconv"
	"strings"
)

type DiskLocation struct {
	Directory      string
	MaxVolumeCount int
	volumes        map[VolumeId]*Volume
}

func (mn *DiskLocation) reset() {
}

type MasterNodes struct {
	nodes    []string
	lastNode int
}

func NewMasterNodes(bootstrapNode string) (mn *MasterNodes) {
	mn = &MasterNodes{nodes: []string{bootstrapNode}, lastNode: -1}
	return
}
func (mn *MasterNodes) reset() {
	if len(mn.nodes) > 1 && mn.lastNode > 0 {
		mn.lastNode = -mn.lastNode
	}
}
func (mn *MasterNodes) findMaster() (string, error) {
	if len(mn.nodes) == 0 {
		return "", errors.New("No master node found!")
	}
	if mn.lastNode < 0 {
		for _, m := range mn.nodes {
			if masters, e := operation.ListMasters(m); e == nil {
				if len(masters) == 0 {
					continue
				}
				mn.nodes = masters
				mn.lastNode = rand.Intn(len(mn.nodes))
				glog.V(2).Info("current master node is :", mn.nodes[mn.lastNode])
				break
			}
		}
	}
	if mn.lastNode < 0 {
		return "", errors.New("No master node avalable!")
	}
	return mn.nodes[mn.lastNode], nil
}

type Store struct {
	Port            int
	Ip              string
	PublicUrl       string
	Locations       []*DiskLocation
	dataCenter      string //optional informaton, overwriting master setting if exists
	rack            string //optional information, overwriting master setting if exists
	connected       bool
	volumeSizeLimit uint64 //read from the master
	masterNodes     *MasterNodes
}

func NewStore(port int, ip, publicUrl string, dirnames []string, maxVolumeCounts []int) (s *Store) {
	s = &Store{Port: port, Ip: ip, PublicUrl: publicUrl}
	s.Locations = make([]*DiskLocation, 0)
	for i := 0; i < len(dirnames); i++ {
		location := &DiskLocation{Directory: dirnames[i], MaxVolumeCount: maxVolumeCounts[i]}
		location.volumes = make(map[VolumeId]*Volume)
		location.loadExistingVolumes()
		s.Locations = append(s.Locations, location)
	}
	return
}
func (s *Store) AddVolume(volumeListString string, collection string, replicaPlacement string) error {
	rt, e := NewReplicaPlacementFromString(replicaPlacement)
	if e != nil {
		return e
	}
	for _, range_string := range strings.Split(volumeListString, ",") {
		if strings.Index(range_string, "-") < 0 {
			id_string := range_string
			id, err := NewVolumeId(id_string)
			if err != nil {
				return fmt.Errorf("Volume Id %s is not a valid unsigned integer!", id_string)
			}
			e = s.addVolume(VolumeId(id), collection, rt)
		} else {
			pair := strings.Split(range_string, "-")
			start, start_err := strconv.ParseUint(pair[0], 10, 64)
			if start_err != nil {
				return fmt.Errorf("Volume Start Id %s is not a valid unsigned integer!", pair[0])
			}
			end, end_err := strconv.ParseUint(pair[1], 10, 64)
			if end_err != nil {
				return fmt.Errorf("Volume End Id %s is not a valid unsigned integer!", pair[1])
			}
			for id := start; id <= end; id++ {
				if err := s.addVolume(VolumeId(id), collection, rt); err != nil {
					e = err
				}
			}
		}
	}
	return e
}
func (s *Store) DeleteCollection(collection string) (e error) {
	for _, location := range s.Locations {
		for k, v := range location.volumes {
			if v.Collection == collection {
				e = v.Destroy()
				if e != nil {
					return
				}
				delete(location.volumes, k)
			}
		}
	}
	return
}
func (s *Store) findVolume(vid VolumeId) *Volume {
	for _, location := range s.Locations {
		if v, found := location.volumes[vid]; found {
			return v
		}
	}
	return nil
}
func (s *Store) findFreeLocation() (ret *DiskLocation) {
	max := 0
	for _, location := range s.Locations {
		currentFreeCount := location.MaxVolumeCount - len(location.volumes)
		if currentFreeCount > max {
			max = currentFreeCount
			ret = location
		}
	}
	return ret
}
func (s *Store) addVolume(vid VolumeId, collection string, replicaPlacement *ReplicaPlacement) error {
	if s.findVolume(vid) != nil {
		return fmt.Errorf("Volume Id %d already exists!", vid)
	}
	if location := s.findFreeLocation(); location != nil {
		glog.V(0).Infoln("In dir", location.Directory, "adds volume =", vid, ", collection =", collection, ", replicaPlacement =", replicaPlacement)
		if volume, err := NewVolume(location.Directory, collection, vid, replicaPlacement); err == nil {
			location.volumes[vid] = volume
			return nil
		} else {
			return err
		}
	}
	return fmt.Errorf("No more free space left")
}

func (s *Store) CheckCompactVolume(volumeIdString string, garbageThresholdString string) (error, bool) {
	vid, err := NewVolumeId(volumeIdString)
	if err != nil {
		return fmt.Errorf("Volume Id %s is not a valid unsigned integer!", volumeIdString), false
	}
	garbageThreshold, e := strconv.ParseFloat(garbageThresholdString, 32)
	if e != nil {
		return fmt.Errorf("garbageThreshold %s is not a valid float number!", garbageThresholdString), false
	}
	if v := s.findVolume(vid); v != nil {
		glog.V(3).Infoln(vid, "garbage level is", v.garbageLevel())
		return nil, garbageThreshold < v.garbageLevel()
	}
	return fmt.Errorf("volume id %d is not found during check compact!", vid), false
}
func (s *Store) CompactVolume(volumeIdString string) error {
	vid, err := NewVolumeId(volumeIdString)
	if err != nil {
		return fmt.Errorf("Volume Id %s is not a valid unsigned integer!", volumeIdString)
	}
	if v := s.findVolume(vid); v != nil {
		return v.Compact()
	}
	return fmt.Errorf("volume id %d is not found during compact!", vid)
}
func (s *Store) CommitCompactVolume(volumeIdString string) error {
	vid, err := NewVolumeId(volumeIdString)
	if err != nil {
		return fmt.Errorf("Volume Id %s is not a valid unsigned integer!", volumeIdString)
	}
	if v := s.findVolume(vid); v != nil {
		return v.commitCompact()
	}
	return fmt.Errorf("volume id %d is not found during commit compact!", vid)
}
func (s *Store) FreezeVolume(volumeIdString string) error {
	vid, err := NewVolumeId(volumeIdString)
	if err != nil {
		return fmt.Errorf("Volume Id %s is not a valid unsigned integer!", volumeIdString)
	}
	if v := s.findVolume(vid); v != nil {
		if v.readOnly {
			return fmt.Errorf("Volume %s is already read-only", volumeIdString)
		}
		return v.freeze()
	}
	return fmt.Errorf("volume id %d is not found during freeze!", vid)
}
func (l *DiskLocation) loadExistingVolumes() {
	if dirs, err := ioutil.ReadDir(l.Directory); err == nil {
		for _, dir := range dirs {
			name := dir.Name()
			if !dir.IsDir() && strings.HasSuffix(name, ".dat") {
				collection := ""
				base := name[:len(name)-len(".dat")]
				i := strings.Index(base, "_")
				if i > 0 {
					collection, base = base[0:i], base[i+1:]
				}
				if vid, err := NewVolumeId(base); err == nil {
					if l.volumes[vid] == nil {
						if v, e := NewVolume(l.Directory, collection, vid, nil); e == nil {
							l.volumes[vid] = v
							glog.V(0).Infoln("data file", l.Directory+"/"+name, "replicaPlacement =", v.ReplicaPlacement, "version =", v.Version(), "size =", v.Size())
						}
					}
				}
			}
		}
	}
	glog.V(0).Infoln("Store started on dir:", l.Directory, "with", len(l.volumes), "volumes", "max", l.MaxVolumeCount)
}
func (s *Store) Status() []*VolumeInfo {
	var stats []*VolumeInfo
	for _, location := range s.Locations {
		for k, v := range location.volumes {
			s := &VolumeInfo{Id: VolumeId(k), Size: v.ContentSize(),
				Collection:       v.Collection,
				ReplicaPlacement: v.ReplicaPlacement,
				Version:          v.Version(),
				FileCount:        v.nm.FileCount(),
				DeleteCount:      v.nm.DeletedCount(),
				DeletedByteCount: v.nm.DeletedSize(),
				ReadOnly:         v.readOnly}
			stats = append(stats, s)
		}
	}
	return stats
}

func (s *Store) SetDataCenter(dataCenter string) {
	s.dataCenter = dataCenter
}
func (s *Store) SetRack(rack string) {
	s.rack = rack
}

func (s *Store) SetBootstrapMaster(bootstrapMaster string) {
	s.masterNodes = NewMasterNodes(bootstrapMaster)
}
func (s *Store) Join() (masterNode string, e error) {
	masterNode, e = s.masterNodes.findMaster()
	if e != nil {
		return
	}
	var volumeMessages []*operation.VolumeInformationMessage
	maxVolumeCount := 0
	var maxFileKey uint64
	for _, location := range s.Locations {
		maxVolumeCount = maxVolumeCount + location.MaxVolumeCount
		for k, v := range location.volumes {
			volumeMessage := &operation.VolumeInformationMessage{
				Id:               proto.Uint32(uint32(k)),
				Size:             proto.Uint64(uint64(v.Size())),
				Collection:       proto.String(v.Collection),
				FileCount:        proto.Uint64(uint64(v.nm.FileCount())),
				DeleteCount:      proto.Uint64(uint64(v.nm.DeletedCount())),
				DeletedByteCount: proto.Uint64(v.nm.DeletedSize()),
				ReadOnly:         proto.Bool(v.readOnly),
				ReplicaPlacement: proto.Uint32(uint32(v.ReplicaPlacement.Byte())),
				Version:          proto.Uint32(uint32(v.Version())),
			}
			volumeMessages = append(volumeMessages, volumeMessage)
			if maxFileKey < v.nm.MaxFileKey() {
				maxFileKey = v.nm.MaxFileKey()
			}
		}
	}

	joinMessage := &operation.JoinMessage{
		IsInit:         proto.Bool(!s.connected),
		Ip:             proto.String(s.Ip),
		Port:           proto.Uint32(uint32(s.Port)),
		PublicUrl:      proto.String(s.PublicUrl),
		MaxVolumeCount: proto.Uint32(uint32(maxVolumeCount)),
		MaxFileKey:     proto.Uint64(maxFileKey),
		DataCenter:     proto.String(s.dataCenter),
		Rack:           proto.String(s.rack),
		Volumes:        volumeMessages,
	}

	data, err := proto.Marshal(joinMessage)
	if err != nil {
		return "", err
	}

	jsonBlob, err := util.PostBytes("http://"+masterNode+"/dir/join", data)
	if err != nil {
		s.masterNodes.reset()
		return "", err
	}
	var ret operation.JoinResult
	if err := json.Unmarshal(jsonBlob, &ret); err != nil {
		return masterNode, err
	}
	if ret.Error != "" {
		return masterNode, errors.New(ret.Error)
	}
	s.volumeSizeLimit = ret.VolumeSizeLimit
	s.connected = true
	return
}
func (s *Store) Close() {
	for _, location := range s.Locations {
		for _, v := range location.volumes {
			v.Close()
		}
	}
}
func (s *Store) Write(i VolumeId, n *Needle) (size uint32, err error) {
	if v := s.findVolume(i); v != nil {
		if v.readOnly {
			err = fmt.Errorf("Volume %d is read only!", i)
			return
		} else {
			if MaxPossibleVolumeSize >= v.ContentSize()+uint64(size) {
				size, err = v.write(n)
			} else {
				err = fmt.Errorf("Volume Size Limit %d Exceeded! Current size is %d", s.volumeSizeLimit, v.ContentSize())
			}
			if s.volumeSizeLimit < v.ContentSize()+3*uint64(size) {
				glog.V(0).Infoln("volume", i, "size", v.ContentSize(), "will exceed limit", s.volumeSizeLimit)
				if _, e := s.Join(); e != nil {
					glog.V(0).Infoln("error when reporting size:", e)
				}
			}
		}
		return
	}
	glog.V(0).Infoln("volume", i, "not found!")
	err = fmt.Errorf("Volume %d not found!", i)
	return
}
func (s *Store) Delete(i VolumeId, n *Needle) (uint32, error) {
	if v := s.findVolume(i); v != nil && !v.readOnly {
		return v.delete(n)
	}
	return 0, nil
}
func (s *Store) Read(i VolumeId, n *Needle) (int, error) {
	if v := s.findVolume(i); v != nil {
		return v.read(n)
	}
	return 0, fmt.Errorf("Volume %v not found!", i)
}
func (s *Store) GetVolume(i VolumeId) *Volume {
	return s.findVolume(i)
}

func (s *Store) HasVolume(i VolumeId) bool {
	v := s.findVolume(i)
	return v != nil
}
<|endoftext|>