<commit_before>package label_test

import (
	"context"
	"fmt"
	"reflect"
	"testing"

	"github.com/influxdata/influxdb/v2"
	"github.com/influxdata/influxdb/v2/inmem"
	"github.com/influxdata/influxdb/v2/kit/platform"
	"github.com/influxdata/influxdb/v2/kv"
	"github.com/influxdata/influxdb/v2/kv/migration/all"
	"github.com/influxdata/influxdb/v2/label"
	"github.com/influxdata/influxdb/v2/mock"
	"go.uber.org/zap/zaptest"
)

func TestLabels(t *testing.T) {
	setup := func(t *testing.T, store *label.Store, tx kv.Tx) {
		for i := 1; i <= 10; i++ {
			mock.SetIDForFunc(&store.IDGenerator, platform.ID(i), func() {
				err := store.CreateLabel(context.Background(), tx, &influxdb.Label{
					Name:  fmt.Sprintf("labelname%d", i),
					OrgID: platform.ID(i),
				})

				if err != nil {
					t.Fatal(err)
				}
			})
		}
	}

	setupForList := func(t *testing.T, store *label.Store, tx kv.Tx) {
		setup(t, store, tx)

		mock.SetIDForFunc(&store.IDGenerator, platform.ID(11), func() {
			err := store.CreateLabel(context.Background(), tx, &influxdb.Label{
				Name:  fmt.Sprintf("labelname%d", 11),
				OrgID: platform.ID(5),
			})
			if err != nil {
				t.Fatal(err)
			}
		})
	}

	tt := []struct {
		name    string
		setup   func(*testing.T, *label.Store, kv.Tx)
		update  func(*testing.T, *label.Store, kv.Tx)
		results func(*testing.T, *label.Store, kv.Tx)
	}{
		{
			name:  "create",
			setup: setup,
			results: func(t *testing.T, store *label.Store, tx kv.Tx) {
				labels, err := store.ListLabels(context.Background(), tx, influxdb.LabelFilter{})
				if err != nil {
					t.Fatal(err)
				}

				if len(labels) != 10 {
					t.Fatalf("expected 10 labels, got: %d", len(labels))
				}

				expected := []*influxdb.Label{}
				for i := 1; i <= 10; i++ {
					expected = append(expected, &influxdb.Label{
						ID:    platform.ID(i),
						Name:  fmt.Sprintf("labelname%d", i),
						OrgID: platform.ID(i),
					})
				}
				if !reflect.DeepEqual(labels, expected) {
					t.Fatalf("expected identical labels: \n%+v\n%+v", labels, expected)
				}
			},
		},
		{
			name:  "get",
			setup: setup,
			results: func(t *testing.T, store *label.Store, tx kv.Tx) {
				label, err := store.GetLabel(context.Background(), tx, platform.ID(1))
				if err != nil {
					t.Fatal(err)
				}

				expected := &influxdb.Label{
					ID:    platform.ID(1),
					Name:  "labelname1",
					OrgID: platform.ID(1),
				}

				if !reflect.DeepEqual(label, expected) {
					t.Fatalf("expected identical label: \n%+v\n%+v", label, expected)
				}
			},
		},
		{
			name:  "list",
			setup: setupForList,
			results: func(t *testing.T, store *label.Store, tx kv.Tx) {
				// list all
				labels, err := store.ListLabels(context.Background(), tx, influxdb.LabelFilter{})
				if err != nil {
					t.Fatal(err)
				}

				if len(labels) != 11 {
					t.Fatalf("expected 11 labels, got: %d", len(labels))
				}

				expected := []*influxdb.Label{}
				for i := 1; i <= 10; i++ {
					expected = append(expected, &influxdb.Label{
						ID:    platform.ID(i),
						Name:  fmt.Sprintf("labelname%d", i),
						OrgID: platform.ID(i),
					})
				}
				expected = append(expected, &influxdb.Label{
					ID:    platform.ID(11),
					Name:  fmt.Sprintf("labelname%d", 11),
					OrgID: platform.ID(5),
				})

				if !reflect.DeepEqual(labels, expected) {
					t.Fatalf("expected identical labels: \n%+v\n%+v", labels, expected)
				}

				// filter by name
				l, err := store.ListLabels(context.Background(), tx, influxdb.LabelFilter{Name: "labelname5"})
				if err != nil {
					t.Fatal(err)
				}

				if len(l) != 1 {
					t.Fatalf("expected 1 label, got: %d", len(l))
				}

				expectedLabel := []*influxdb.Label{&influxdb.Label{
					ID:    platform.ID(5),
					Name:  "labelname5",
					OrgID: platform.ID(5),
				}}
				if !reflect.DeepEqual(l, expectedLabel) {
					t.Fatalf("label returned by list did not match expected: \n%+v\n%+v", l, expectedLabel)
				}

				// filter by org id
				id := platform.ID(5)
				l, err = store.ListLabels(context.Background(), tx, influxdb.LabelFilter{OrgID: &id})
				if err != nil {
					t.Fatal(err)
				}

				if len(l) != 2 {
					t.Fatalf("expected 2 labels, got: %d", len(l))
				}

				expectedLabel = []*influxdb.Label{
					&influxdb.Label{
						ID:    platform.ID(5),
						Name:  "labelname5",
						OrgID: platform.ID(5)},
					{
						ID:    platform.ID(11),
						Name:  "labelname11",
						OrgID: platform.ID(5),
					}}
				if !reflect.DeepEqual(l, expectedLabel) {
					t.Fatalf("label returned by list did not match expected: \n%+v\n%+v", l, expectedLabel)
				}
			},
		},
		{
			name:  "update",
			setup: setup,
			update: func(t *testing.T, store *label.Store, tx kv.Tx) {
				upd := influxdb.LabelUpdate{Name: "newName"}
				updated, err := store.UpdateLabel(context.Background(), tx, platform.ID(1), upd)
				if err != nil {
					t.Fatal(err)
				}

				if updated.Name != upd.Name {
					t.Fatalf("expected updated name %s, got: %s", upd.Name, updated.Name)
				}
			},
			results: func(t *testing.T, store *label.Store, tx kv.Tx) {
				la, err := store.GetLabel(context.Background(), tx, platform.ID(1))
				if err != nil {
					t.Fatal(err)
				}

				if la.Name != "newName" {
					t.Fatalf("expected update name to be %s, got: %s", "newName", la.Name)
				}
			},
		},
		{
			name:  "delete",
			setup: setup,
			update: func(t *testing.T, store *label.Store, tx kv.Tx) {
				err := store.DeleteLabel(context.Background(), tx, platform.ID(5))
				if err != nil {
					t.Fatal(err)
				}

				err = store.DeleteLabel(context.Background(), tx, platform.ID(5))
				if err != label.ErrLabelNotFound {
					t.Fatal("expected label not found error when deleting bucket that has already been deleted, got: ", err)
				}
			},
			results: func(t *testing.T, store *label.Store, tx kv.Tx) {
				l, err := store.ListLabels(context.Background(), tx, influxdb.LabelFilter{})
				if err != nil {
					t.Fatal(err)
				}

				if len(l) != 9 {
					t.Fatalf("expected 2 labels, got: %d", len(l))
				}
			},
		},
	}

	for _, testScenario := range tt {
		t.Run(testScenario.name, func(t *testing.T) {
			t.Parallel()

			store := inmem.NewKVStore()
			if err := all.Up(context.Background(), zaptest.NewLogger(t), store); err != nil {
				t.Fatal(err)
			}

			ts, err := label.NewStore(store)
			if err != nil {
				t.Fatal(err)
			}

			// setup
			if testScenario.setup != nil {
				err := ts.Update(context.Background(), func(tx kv.Tx) error {
					testScenario.setup(t, ts, tx)
					return nil
				})

				if err != nil {
					t.Fatal(err)
				}
			}

			// update
			if testScenario.update != nil {
				err := ts.Update(context.Background(), func(tx kv.Tx) error {
					testScenario.update(t, ts, tx)
					return nil
				})

				if err != nil {
					t.Fatal(err)
				}
			}

			// results
			if testScenario.results != nil {
				err := ts.View(context.Background(), func(tx kv.Tx) error {
					testScenario.results(t, ts, tx)
					return nil
				})

				if err != nil {
					t.Fatal(err)
				}
			}
		})
	}
}
<commit_msg>test(label): Invalid closure capture (#23353)<commit_after>package label_test

import (
	"context"
	"fmt"
	"reflect"
	"testing"

	"github.com/influxdata/influxdb/v2"
	"github.com/influxdata/influxdb/v2/inmem"
	"github.com/influxdata/influxdb/v2/kit/platform"
	"github.com/influxdata/influxdb/v2/kv"
	"github.com/influxdata/influxdb/v2/kv/migration/all"
	"github.com/influxdata/influxdb/v2/label"
	"github.com/influxdata/influxdb/v2/mock"
	"go.uber.org/zap/zaptest"
)

func TestLabels(t *testing.T) {
	setup := func(t *testing.T, store *label.Store, tx kv.Tx) {
		for i := 1; i <= 10; i++ {
			mock.SetIDForFunc(&store.IDGenerator, platform.ID(i), func() {
				err := store.CreateLabel(context.Background(), tx, &influxdb.Label{
					Name:  fmt.Sprintf("labelname%d", i),
					OrgID: platform.ID(i),
				})

				if err != nil {
					t.Fatal(err)
				}
			})
		}
	}

	setupForList := func(t *testing.T, store *label.Store, tx kv.Tx) {
		setup(t, store, tx)

		mock.SetIDForFunc(&store.IDGenerator, platform.ID(11), func() {
			err := store.CreateLabel(context.Background(), tx, &influxdb.Label{
				Name:  fmt.Sprintf("labelname%d", 11),
				OrgID: platform.ID(5),
			})
			if err != nil {
				t.Fatal(err)
			}
		})
	}

	tt := []struct {
		name    string
		setup   func(*testing.T, *label.Store, kv.Tx)
		update  func(*testing.T, *label.Store, kv.Tx)
		results func(*testing.T, *label.Store, kv.Tx)
	}{
		{
			name:  "create",
			setup: setup,
			results: func(t *testing.T, store *label.Store, tx kv.Tx) {
				labels, err := store.ListLabels(context.Background(), tx, influxdb.LabelFilter{})
				if err != nil {
					t.Fatal(err)
				}

				if len(labels) != 10 {
					t.Fatalf("expected 10 labels, got: %d", len(labels))
				}

				expected := []*influxdb.Label{}
				for i := 1; i <= 10; i++ {
					expected = append(expected, &influxdb.Label{
						ID:    platform.ID(i),
						Name:  fmt.Sprintf("labelname%d", i),
						OrgID: platform.ID(i),
					})
				}
				if !reflect.DeepEqual(labels, expected) {
					t.Fatalf("expected identical labels: \n%+v\n%+v", labels, expected)
				}
			},
		},
		{
			name:  "get",
			setup: setup,
			results: func(t *testing.T, store *label.Store, tx kv.Tx) {
				label, err := store.GetLabel(context.Background(), tx, platform.ID(1))
				if err != nil {
					t.Fatal(err)
				}

				expected := &influxdb.Label{
					ID:    platform.ID(1),
					Name:  "labelname1",
					OrgID: platform.ID(1),
				}

				if !reflect.DeepEqual(label, expected) {
					t.Fatalf("expected identical label: \n%+v\n%+v", label, expected)
				}
			},
		},
		{
			name:  "list",
			setup: setupForList,
			results: func(t *testing.T, store *label.Store, tx kv.Tx) {
				// list all
				labels, err := store.ListLabels(context.Background(), tx, influxdb.LabelFilter{})
				if err != nil {
					t.Fatal(err)
				}

				if len(labels) != 11 {
					t.Fatalf("expected 11 labels, got: %d", len(labels))
				}

				expected := []*influxdb.Label{}
				for i := 1; i <= 10; i++ {
					expected = append(expected, &influxdb.Label{
						ID:    platform.ID(i),
						Name:  fmt.Sprintf("labelname%d", i),
						OrgID: platform.ID(i),
					})
				}
				expected = append(expected, &influxdb.Label{
					ID:    platform.ID(11),
					Name:  fmt.Sprintf("labelname%d", 11),
					OrgID: platform.ID(5),
				})

				if !reflect.DeepEqual(labels, expected) {
					t.Fatalf("expected identical labels: \n%+v\n%+v", labels, expected)
				}

				// filter by name
				l, err := store.ListLabels(context.Background(), tx, influxdb.LabelFilter{Name: "labelname5"})
				if err != nil {
					t.Fatal(err)
				}

				if len(l) != 1 {
					t.Fatalf("expected 1 label, got: %d", len(l))
				}

				expectedLabel := []*influxdb.Label{&influxdb.Label{
					ID:    platform.ID(5),
					Name:  "labelname5",
					OrgID: platform.ID(5),
				}}
				if !reflect.DeepEqual(l, expectedLabel) {
					t.Fatalf("label returned by list did not match expected: \n%+v\n%+v", l, expectedLabel)
				}

				// filter by org id
				id := platform.ID(5)
				l, err = store.ListLabels(context.Background(), tx, influxdb.LabelFilter{OrgID: &id})
				if err != nil {
					t.Fatal(err)
				}

				if len(l) != 2 {
					t.Fatalf("expected 2 labels, got: %d", len(l))
				}

				expectedLabel = []*influxdb.Label{
					&influxdb.Label{
						ID:    platform.ID(5),
						Name:  "labelname5",
						OrgID: platform.ID(5)},
					{
						ID:    platform.ID(11),
						Name:  "labelname11",
						OrgID: platform.ID(5),
					}}
				if !reflect.DeepEqual(l, expectedLabel) {
					t.Fatalf("label returned by list did not match expected: \n%+v\n%+v", l, expectedLabel)
				}
			},
		},
		{
			name:  "update",
			setup: setup,
			update: func(t *testing.T, store *label.Store, tx kv.Tx) {
				upd := influxdb.LabelUpdate{Name: "newName"}
				updated, err := store.UpdateLabel(context.Background(), tx, platform.ID(1), upd)
				if err != nil {
					t.Fatal(err)
				}

				if updated.Name != upd.Name {
					t.Fatalf("expected updated name %s, got: %s", upd.Name, updated.Name)
				}
			},
			results: func(t *testing.T, store *label.Store, tx kv.Tx) {
				la, err := store.GetLabel(context.Background(), tx, platform.ID(1))
				if err != nil {
					t.Fatal(err)
				}

				if la.Name != "newName" {
					t.Fatalf("expected update name to be %s, got: %s", "newName", la.Name)
				}
			},
		},
		{
			name:  "delete",
			setup: setup,
			update: func(t *testing.T, store *label.Store, tx kv.Tx) {
				err := store.DeleteLabel(context.Background(), tx, platform.ID(5))
				if err != nil {
					t.Fatal(err)
				}

				err = store.DeleteLabel(context.Background(), tx, platform.ID(5))
				if err != label.ErrLabelNotFound {
					t.Fatal("expected label not found error when deleting bucket that has already been deleted, got: ", err)
				}
			},
			results: func(t *testing.T, store *label.Store, tx kv.Tx) {
				l, err := store.ListLabels(context.Background(), tx, influxdb.LabelFilter{})
				if err != nil {
					t.Fatal(err)
				}

				if len(l) != 9 {
					t.Fatalf("expected 9 labels, got: %d", len(l))
				}
			},
		},
	}

	for _, ts := range tt {
		testScenario := ts
		t.Run(testScenario.name, func(t *testing.T) {
			t.Parallel()

			store := inmem.NewKVStore()
			if err := all.Up(context.Background(), zaptest.NewLogger(t), store); err != nil {
				t.Fatal(err)
			}

			ts, err := label.NewStore(store)
			if err != nil {
				t.Fatal(err)
			}

			// setup
			if testScenario.setup != nil {
				err := ts.Update(context.Background(), func(tx kv.Tx) error {
					testScenario.setup(t, ts, tx)
					return nil
				})

				if err != nil {
					t.Fatal(err)
				}
			}

			// update
			if testScenario.update != nil {
				err := ts.Update(context.Background(), func(tx kv.Tx) error {
					testScenario.update(t, ts, tx)
					return nil
				})

				if err != nil {
					t.Fatal(err)
				}
			}

			// results
			if testScenario.results != nil {
				err := ts.View(context.Background(), func(tx kv.Tx) error {
					testScenario.results(t, ts, tx)
					return nil
				})

				if err != nil {
					t.Fatal(err)
				}
			}
		})
	}
}
<|endoftext|>


<commit_before>// Copyright (c) 2018 Ashley Jeffs
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package processor

import (
	"fmt"

	"github.com/Jeffail/benthos/lib/log"
	"github.com/Jeffail/benthos/lib/message"
	"github.com/Jeffail/benthos/lib/metrics"
	"github.com/Jeffail/benthos/lib/response"
	"github.com/Jeffail/benthos/lib/types"
	"github.com/Jeffail/benthos/lib/util/http/client"
)

//------------------------------------------------------------------------------

func init() {
	Constructors[TypeHTTP] = TypeSpec{
		constructor: NewHTTP,
		description: `
Performs an HTTP request using a message batch as the request body, and replaces
the original message parts with the body of the response.

If the batch contains only a single message part then it will be sent as the
body of the request. If the batch contains multiple messages then they will be
sent as a multipart HTTP request using a ` + "`Content-Type: multipart`" + `
header.

If you are sending batches and wish to avoid this behaviour then you can set the
` + "`parallel`" + ` flag to ` + "`true`" + ` and the messages of a batch will
be sent as individual requests in parallel. You can also cap the max number of
parallel requests with ` + "`max_parallel`" + `. Alternatively, you can use the
` + "[`archive`](#archive)" + ` processor to create a single message
from the batch.

The URL and header values of this type can be dynamically set using function
interpolations described [here](../config_interpolation.md#functions).

In order to map or encode the payload to a specific request body, and map the
response back into the original payload instead of replacing it entirely, you
can use the ` + "[`process_map`](#process_map)" + ` or
 ` + "[`process_field`](#process_field)" + ` processors.`,
	}
}

//------------------------------------------------------------------------------

// HTTPConfig contains configuration fields for the HTTP processor.
type HTTPConfig struct {
	Client      client.Config `json:"request" yaml:"request"`
	Parallel    bool          `json:"parallel" yaml:"parallel"`
	MaxParallel int           `json:"max_parallel" yaml:"max_parallel"`
}

// NewHTTPConfig returns a HTTPConfig with default values.
func NewHTTPConfig() HTTPConfig {
	return HTTPConfig{
		Client:      client.NewConfig(),
		Parallel:    false,
		MaxParallel: 0,
	}
}

//------------------------------------------------------------------------------

// HTTP is a processor that performs an HTTP request using the message as the
// request body, and returns the response.
type HTTP struct {
	client *client.Type

	parallel bool
	max      int

	conf  Config
	log   log.Modular
	stats metrics.Type

	mCount     metrics.StatCounter
	mErrHTTP   metrics.StatCounter
	mErr       metrics.StatCounter
	mSucc      metrics.StatCounter
	mSent      metrics.StatCounter
	mSentParts metrics.StatCounter
}

// NewHTTP returns a HTTP processor.
func NewHTTP(
	conf Config, mgr types.Manager, log log.Modular, stats metrics.Type,
) (Type, error) {
	g := &HTTP{
		conf:  conf,
		log:   log.NewModule(".processor.http"),
		stats: stats,

		parallel: conf.HTTP.Parallel,
		max:      conf.HTTP.MaxParallel,

		mCount:     stats.GetCounter("processor.http.count"),
		mSucc:      stats.GetCounter("processor.http.success"),
		mErr:       stats.GetCounter("processor.http.error"),
		mErrHTTP:   stats.GetCounter("processor.http.error.http"),
		mSent:      stats.GetCounter("processor.http.sent"),
		mSentParts: stats.GetCounter("processor.http.parts.sent"),
	}
	var err error
	if g.client, err = client.New(
		conf.HTTP.Client,
		client.OptSetLogger(g.log),
		client.OptSetStats(metrics.Namespaced(g.stats, "processor.http")),
	); err != nil {
		return nil, err
	}
	return g, nil
}

//------------------------------------------------------------------------------

// ProcessMessage applies the processor to a message, either creating >0
// resulting messages or a response to be sent back to the message source.
func (h *HTTP) ProcessMessage(msg types.Message) ([]types.Message, types.Response) {
	h.mCount.Incr(1)
	var responseMsg types.Message

	if !h.parallel || msg.Len() == 1 {
		// Easy, just do a single request.
		var err error
		if responseMsg, err = h.client.Send(msg); err != nil {
			if err != nil {
				h.mErr.Incr(1)
				h.mErrHTTP.Incr(1)
				return nil, response.NewError(fmt.Errorf(
					"HTTP request '%v' failed: %v", h.conf.HTTP.Client.URL, err,
				))
			}
		}
	} else {
		// Hard, need to do parallel requests limited by max parallelism.
		responseMsg = msg.ShallowCopy()
		results := responseMsg.GetAll()
		reqChan, resChan := make(chan int), make(chan error)

		max := h.max
		if max == 0 {
			max = msg.Len()
		}

		for i := 0; i < max; i++ {
			go func() {
				for index := range reqChan {
					result, err := h.client.Send(message.Lock(msg, index))
					if err == nil && result.Len() != 1 {
						err = fmt.Errorf("unexpected response size: %v", result.Len())
					}
					if err == nil {
						results[index] = result.Get(0)
					}
					resChan <- err
				}
			}()
		}
		go func() {
			for i := 0; i < msg.Len(); i++ {
				reqChan <- i
			}
		}()
		for i := 0; i < msg.Len(); i++ {
			if err := <-resChan; err != nil {
				h.mErrHTTP.Incr(1)
				h.log.Errorf("HTTP parallel request to '%v' failed: %v\n", h.conf.HTTP.Client.URL, err)
			}
		}

		close(reqChan)
		responseMsg = message.New(results)
	}

	if responseMsg.Len() < 1 {
		h.mErr.Incr(1)
		h.mErrHTTP.Incr(1)
		return nil, response.NewError(fmt.Errorf(
			"HTTP response from '%v' was empty", h.conf.HTTP.Client.URL,
		))
	}

	h.mSucc.Incr(1)
	msgs := [1]types.Message{responseMsg}

	h.mSent.Incr(1)
	h.mSentParts.Incr(int64(responseMsg.Len()))
	return msgs[:], nil
}

//------------------------------------------------------------------------------
<commit_msg>Avoid unnecessary goroutines<commit_after>// Copyright (c) 2018 Ashley Jeffs
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package processor

import (
	"fmt"

	"github.com/Jeffail/benthos/lib/log"
	"github.com/Jeffail/benthos/lib/message"
	"github.com/Jeffail/benthos/lib/metrics"
	"github.com/Jeffail/benthos/lib/response"
	"github.com/Jeffail/benthos/lib/types"
	"github.com/Jeffail/benthos/lib/util/http/client"
)

//------------------------------------------------------------------------------

func init() {
	Constructors[TypeHTTP] = TypeSpec{
		constructor: NewHTTP,
		description: `
Performs an HTTP request using a message batch as the request body, and replaces
the original message parts with the body of the response.

If the batch contains only a single message part then it will be sent as the
body of the request. If the batch contains multiple messages then they will be
sent as a multipart HTTP request using a ` + "`Content-Type: multipart`" + `
header.

If you are sending batches and wish to avoid this behaviour then you can set the
` + "`parallel`" + ` flag to ` + "`true`" + ` and the messages of a batch will
be sent as individual requests in parallel. You can also cap the max number of
parallel requests with ` + "`max_parallel`" + `. Alternatively, you can use the
` + "[`archive`](#archive)" + ` processor to create a single message
from the batch.

The URL and header values of this type can be dynamically set using function
interpolations described [here](../config_interpolation.md#functions).

In order to map or encode the payload to a specific request body, and map the
response back into the original payload instead of replacing it entirely, you
can use the ` + "[`process_map`](#process_map)" + ` or
 ` + "[`process_field`](#process_field)" + ` processors.`,
	}
}

//------------------------------------------------------------------------------

// HTTPConfig contains configuration fields for the HTTP processor.
type HTTPConfig struct {
	Client      client.Config `json:"request" yaml:"request"`
	Parallel    bool          `json:"parallel" yaml:"parallel"`
	MaxParallel int           `json:"max_parallel" yaml:"max_parallel"`
}

// NewHTTPConfig returns a HTTPConfig with default values.
func NewHTTPConfig() HTTPConfig {
	return HTTPConfig{
		Client:      client.NewConfig(),
		Parallel:    false,
		MaxParallel: 0,
	}
}

//------------------------------------------------------------------------------

// HTTP is a processor that performs an HTTP request using the message as the
// request body, and returns the response.
type HTTP struct {
	client *client.Type

	parallel bool
	max      int

	conf  Config
	log   log.Modular
	stats metrics.Type

	mCount     metrics.StatCounter
	mErrHTTP   metrics.StatCounter
	mErr       metrics.StatCounter
	mSucc      metrics.StatCounter
	mSent      metrics.StatCounter
	mSentParts metrics.StatCounter
}

// NewHTTP returns a HTTP processor.
func NewHTTP(
	conf Config, mgr types.Manager, log log.Modular, stats metrics.Type,
) (Type, error) {
	g := &HTTP{
		conf:  conf,
		log:   log.NewModule(".processor.http"),
		stats: stats,

		parallel: conf.HTTP.Parallel,
		max:      conf.HTTP.MaxParallel,

		mCount:     stats.GetCounter("processor.http.count"),
		mSucc:      stats.GetCounter("processor.http.success"),
		mErr:       stats.GetCounter("processor.http.error"),
		mErrHTTP:   stats.GetCounter("processor.http.error.http"),
		mSent:      stats.GetCounter("processor.http.sent"),
		mSentParts: stats.GetCounter("processor.http.parts.sent"),
	}
	var err error
	if g.client, err = client.New(
		conf.HTTP.Client,
		client.OptSetLogger(g.log),
		client.OptSetStats(metrics.Namespaced(g.stats, "processor.http")),
	); err != nil {
		return nil, err
	}
	return g, nil
}

//------------------------------------------------------------------------------

// ProcessMessage applies the processor to a message, either creating >0
// resulting messages or a response to be sent back to the message source.
func (h *HTTP) ProcessMessage(msg types.Message) ([]types.Message, types.Response) {
	h.mCount.Incr(1)
	var responseMsg types.Message

	if !h.parallel || msg.Len() == 1 {
		// Easy, just do a single request.
		var err error
		if responseMsg, err = h.client.Send(msg); err != nil {
			if err != nil {
				h.mErr.Incr(1)
				h.mErrHTTP.Incr(1)
				return nil, response.NewError(fmt.Errorf(
					"HTTP request '%v' failed: %v", h.conf.HTTP.Client.URL, err,
				))
			}
		}
	} else {
		// Hard, need to do parallel requests limited by max parallelism.
		responseMsg = msg.ShallowCopy()
		results := responseMsg.GetAll()
		reqChan, resChan := make(chan int), make(chan error)

		max := h.max
		if max == 0 || msg.Len() < max {
			max = msg.Len()
		}

		for i := 0; i < max; i++ {
			go func() {
				for index := range reqChan {
					result, err := h.client.Send(message.Lock(msg, index))
					if err == nil && result.Len() != 1 {
						err = fmt.Errorf("unexpected response size: %v", result.Len())
					}
					if err == nil {
						results[index] = result.Get(0)
					}
					resChan <- err
				}
			}()
		}
		go func() {
			for i := 0; i < msg.Len(); i++ {
				reqChan <- i
			}
		}()
		for i := 0; i < msg.Len(); i++ {
			if err := <-resChan; err != nil {
				h.mErrHTTP.Incr(1)
				h.log.Errorf("HTTP parallel request to '%v' failed: %v\n", h.conf.HTTP.Client.URL, err)
			}
		}

		close(reqChan)
		responseMsg = message.New(results)
	}

	if responseMsg.Len() < 1 {
		h.mErr.Incr(1)
		h.mErrHTTP.Incr(1)
		return nil, response.NewError(fmt.Errorf(
			"HTTP response from '%v' was empty", h.conf.HTTP.Client.URL,
		))
	}

	h.mSucc.Incr(1)
	msgs := [1]types.Message{responseMsg}

	h.mSent.Incr(1)
	h.mSentParts.Incr(int64(responseMsg.Len()))
	return msgs[:], nil
}

//------------------------------------------------------------------------------
<|endoftext|>


<commit_before>package rcmgr

import (
	"fmt"

	"github.com/libp2p/go-libp2p-core/network"
	"github.com/libp2p/go-libp2p-core/peer"
	"github.com/libp2p/go-libp2p-core/protocol"
)

type ResourceManager struct {
	system    *SystemScope
	transient *TransientScope
}

type SystemScope struct {
	*ResourceScope
}

var _ network.ResourceScope = (*SystemScope)(nil)

type TransientScope struct {
	*ResourceScope

	system *SystemScope
}

var _ network.ResourceScope = (*TransientScope)(nil)

type ServiceScope struct {
	*ResourceScope

	name   string
	system *SystemScope
}

var _ network.ServiceScope = (*ServiceScope)(nil)

type ProtocolScope struct {
	*ResourceScope

	proto  protocol.ID
	system *SystemScope
}

var _ network.ProtocolScope = (*ProtocolScope)(nil)

type PeerScope struct {
	*ResourceScope

	peer      peer.ID
	rcmgr     *ResourceManager
	system    *SystemScope
	transient *TransientScope
}

var _ network.PeerScope = (*PeerScope)(nil)

type ConnectionScope struct {
	*ResourceScope

	dir       network.Direction
	rcmgr     *ResourceManager
	system    *SystemScope
	transient *TransientScope
	peer      *PeerScope
}

var _ network.ConnectionScope = (*ConnectionScope)(nil)

type StreamScope struct {
	*ResourceScope

	dir       network.Direction
	rcmgr     *ResourceManager
	system    *SystemScope
	transient *TransientScope
	peer      *PeerScope
	svc       *ServiceScope
	proto     *ProtocolScope
}

var _ network.StreamScope = (*StreamScope)(nil)

func (r *ResourceManager) getProtocolScope(proto protocol.ID) *ProtocolScope {
	// TODO
	return nil
}

func (r *ResourceManager) getServiceScope(svc string) *ServiceScope {
	// TODO
	return nil
}

func (r *ResourceManager) getPeerScope(p peer.ID) *PeerScope {
	// TODO
	return nil
}

func (r *ResourceManager) getConnLimit() Limit {
	// TODO
	return nil
}

func (r *ResourceManager) getStreamLimit(p peer.ID) Limit {
	// TODO
	return nil
}

func (r *ResourceManager) OpenConnection(dir network.Direction, usefd bool) (network.ConnectionScope, error) {
	conn := NewConnectionScope(dir, r.getConnLimit(), r)

	if err := conn.AddConn(dir); err != nil {
		return nil, err
	}

	if err := conn.AddFD(1); err != nil {
		conn.RemoveConn(dir)
		return nil, err
	}

	return conn, nil
}

func NewSystemScope(limit Limit) *SystemScope {
	return &SystemScope{
		ResourceScope: NewResourceScope(limit, nil),
	}
}

func NewTransientScope(limit Limit, system *SystemScope) *TransientScope {
	return &TransientScope{
		ResourceScope: NewResourceScope(limit, []*ResourceScope{system.ResourceScope}),
		system:        system,
	}
}

func NewServiceScope(name string, limit Limit, system *SystemScope) *ServiceScope {
	return &ServiceScope{
		ResourceScope: NewResourceScope(limit, []*ResourceScope{system.ResourceScope}),
		name:          name,
		system:        system,
	}
}

func NewProtocolScope(proto protocol.ID, limit Limit, system *SystemScope) *ProtocolScope {
	return &ProtocolScope{
		ResourceScope: NewResourceScope(limit, []*ResourceScope{system.ResourceScope}),
		proto:         proto,
		system:        system,
	}
}

func NewPeerScope(p peer.ID, limit Limit, rcmgr *ResourceManager) *PeerScope {
	return &PeerScope{
		ResourceScope: NewResourceScope(limit, []*ResourceScope{rcmgr.system.ResourceScope}),
		peer:          p,
		rcmgr:         rcmgr,
		system:        rcmgr.system,
		transient:     rcmgr.transient,
	}
}

func NewConnectionScope(dir network.Direction, limit Limit, rcmgr *ResourceManager) *ConnectionScope {
	return &ConnectionScope{
		ResourceScope: NewResourceScope(limit, []*ResourceScope{rcmgr.transient.ResourceScope, rcmgr.system.ResourceScope}),
		dir:           dir,
		rcmgr:         rcmgr,
		system:        rcmgr.system,
		transient:     rcmgr.transient,
	}
}

func NewStreamScope(dir network.Direction, limit Limit, peer *PeerScope) *StreamScope {
	return &StreamScope{
		ResourceScope: NewResourceScope(limit, []*ResourceScope{peer.ResourceScope, peer.transient.ResourceScope, peer.system.ResourceScope}),
		dir:           dir,
		rcmgr:         peer.rcmgr,
		system:        peer.system,
		transient:     peer.transient,
		peer:          peer,
	}
}

func (s *ServiceScope) Name() string {
	return s.name
}

func (s *ProtocolScope) Protocol() protocol.ID {
	return s.proto
}

func (s *PeerScope) Peer() peer.ID {
	return s.peer
}

func (s *PeerScope) OpenStream(dir network.Direction) (network.StreamScope, error) {
	stream := NewStreamScope(dir, s.rcmgr.getStreamLimit(s.peer), s)
	err := stream.AddStream(dir)
	if err != nil {
		return nil, err
	}

	return stream, nil
}

func (s *ConnectionScope) PeerScope() network.PeerScope {
	s.Lock()
	defer s.Unlock()
	return s.peer
}

func (s *ConnectionScope) SetPeer(p peer.ID) error {
	s.Lock()
	defer s.Unlock()

	if s.peer != nil {
		return fmt.Errorf("connection scope already attached to a peer")
	}
	s.peer = s.rcmgr.getPeerScope(p)

	// juggle resources from transient scope to peer scope
	mem := s.ResourceScope.rc.memory

	var incount, outcount int
	if s.dir == network.DirInbound {
		incount = 1
	} else {
		outcount = 1
	}

	if err := s.peer.ReserveMemoryForChild(mem); err != nil {
		return err
	}
	if err := s.peer.AddConnForChild(incount, outcount); err != nil {
		s.peer.ReleaseMemoryForChild(mem)
		return err
	}
	if err := s.peer.AddFDForChild(1); err != nil {
		s.peer.ReleaseMemoryForChild(mem)
		s.peer.RemoveConnForChild(incount, outcount)
		return err
	}

	s.transient.ReleaseMemoryForChild(mem)
	s.transient.RemoveConnForChild(incount, outcount)
	s.transient.RemoveFDForChild(1)

	// update constraints
	constraints := []*ResourceScope{
		s.peer.ResourceScope,
		s.system.ResourceScope,
	}
	s.ResourceScope.constraints = constraints

	return nil
}

func (s *StreamScope) ProtocolScope() network.ProtocolScope {
	s.Lock()
	defer s.Unlock()
	return s.proto
}

func (s *StreamScope) SetProtocol(proto protocol.ID) error {
	s.Lock()
	defer s.Unlock()

	if s.proto != nil {
		return fmt.Errorf("stream scope already attached to a protocol")
	}

	s.proto = s.rcmgr.getProtocolScope(proto)

	// juggle resources from transient scope to protocol scope
	mem := s.ResourceScope.rc.memory

	var incount, outcount int
	if s.dir == network.DirInbound {
		incount = 1
	} else {
		outcount = 1
	}

	if err := s.proto.ReserveMemoryForChild(mem); err != nil {
		return err
	}
	if err := s.proto.AddStreamForChild(incount, outcount); err != nil {
		s.proto.ReleaseMemoryForChild(mem)
		return err
	}

	s.transient.ReleaseMemoryForChild(mem)
	s.transient.RemoveStreamForChild(incount, outcount)

	// update constraints
	constraints := []*ResourceScope{
		s.peer.ResourceScope,
		s.proto.ResourceScope,
		s.system.ResourceScope,
	}
	s.ResourceScope.constraints = constraints

	return nil
}

func (s *StreamScope) ServiceScope() network.ServiceScope {
	s.Lock()
	defer s.Unlock()
	return s.svc
}

func (s *StreamScope) SetService(svc string) error {
	s.Lock()
	defer s.Unlock()

	if s.proto == nil {
		return fmt.Errorf("stream scope not attached to a protocol")
	}
	if s.svc != nil {
		return fmt.Errorf("stream scope already attached to a service")
	}

	s.svc = s.rcmgr.getServiceScope(svc)

	// reserve resources in service
	mem := s.ResourceScope.rc.memory

	var incount, outcount int
	if s.dir == network.DirInbound {
		incount = 1
	} else {
		outcount = 1
	}

	if err := s.svc.ReserveMemoryForChild(mem); err != nil {
		return err
	}
	if err := s.svc.AddStreamForChild(incount, outcount); err != nil {
		s.svc.ReleaseMemoryForChild(mem)
		return err
	}

	// update constraints
	constraints := []*ResourceScope{
		s.peer.ResourceScope,
		s.proto.ResourceScope,
		s.svc.ResourceScope,
		s.system.ResourceScope,
	}
	s.ResourceScope.constraints = constraints

	return nil
}

func (s *StreamScope) PeerScope() network.PeerScope {
	s.Lock()
	defer s.Unlock()
	return s.peer
}
<commit_msg>only count fds if the connection actually uses an fd<commit_after>package rcmgr

import (
	"fmt"

	"github.com/libp2p/go-libp2p-core/network"
	"github.com/libp2p/go-libp2p-core/peer"
	"github.com/libp2p/go-libp2p-core/protocol"
)

type ResourceManager struct {
	system    *SystemScope
	transient *TransientScope
}

type SystemScope struct {
	*ResourceScope
}

var _ network.ResourceScope = (*SystemScope)(nil)

type TransientScope struct {
	*ResourceScope

	system *SystemScope
}

var _ network.ResourceScope = (*TransientScope)(nil)

type ServiceScope struct {
	*ResourceScope

	name   string
	system *SystemScope
}

var _ network.ServiceScope = (*ServiceScope)(nil)

type ProtocolScope struct {
	*ResourceScope

	proto  protocol.ID
	system *SystemScope
}

var _ network.ProtocolScope = (*ProtocolScope)(nil)

type PeerScope struct {
	*ResourceScope

	peer      peer.ID
	rcmgr     *ResourceManager
	system    *SystemScope
	transient *TransientScope
}

var _ network.PeerScope = (*PeerScope)(nil)

type ConnectionScope struct {
	*ResourceScope

	dir       network.Direction
	usefd     bool
	rcmgr     *ResourceManager
	system    *SystemScope
	transient *TransientScope
	peer      *PeerScope
}

var _ network.ConnectionScope = (*ConnectionScope)(nil)

type StreamScope struct {
	*ResourceScope

	dir       network.Direction
	rcmgr     *ResourceManager
	system    *SystemScope
	transient *TransientScope
	peer      *PeerScope
	svc       *ServiceScope
	proto     *ProtocolScope
}

var _ network.StreamScope = (*StreamScope)(nil)

func (r *ResourceManager) getProtocolScope(proto protocol.ID) *ProtocolScope {
	// TODO
	return nil
}

func (r *ResourceManager) getServiceScope(svc string) *ServiceScope {
	// TODO
	return nil
}

func (r *ResourceManager) getPeerScope(p peer.ID) *PeerScope {
	// TODO
	return nil
}

func (r *ResourceManager) getConnLimit() Limit {
	// TODO
	return nil
}

func (r *ResourceManager) getStreamLimit(p peer.ID) Limit {
	// TODO
	return nil
}

func (r *ResourceManager) OpenConnection(dir network.Direction, usefd bool) (network.ConnectionScope, error) {
	conn := NewConnectionScope(dir, usefd, r.getConnLimit(), r)

	if err := conn.AddConn(dir); err != nil {
		return nil, err
	}

	if usefd {
		if err := conn.AddFD(1); err != nil {
			conn.RemoveConn(dir)
			return nil, err
		}
	}

	return conn, nil
}

func NewSystemScope(limit Limit) *SystemScope {
	return &SystemScope{
		ResourceScope: NewResourceScope(limit, nil),
	}
}

func NewTransientScope(limit Limit, system *SystemScope) *TransientScope {
	return &TransientScope{
		ResourceScope: NewResourceScope(limit, []*ResourceScope{system.ResourceScope}),
		system:        system,
	}
}

func NewServiceScope(name string, limit Limit, system *SystemScope) *ServiceScope {
	return &ServiceScope{
		ResourceScope: NewResourceScope(limit, []*ResourceScope{system.ResourceScope}),
		name:          name,
		system:        system,
	}
}

func NewProtocolScope(proto protocol.ID, limit Limit, system *SystemScope) *ProtocolScope {
	return &ProtocolScope{
		ResourceScope: NewResourceScope(limit, []*ResourceScope{system.ResourceScope}),
		proto:         proto,
		system:        system,
	}
}

func NewPeerScope(p peer.ID, limit Limit, rcmgr *ResourceManager) *PeerScope {
	return &PeerScope{
		ResourceScope: NewResourceScope(limit, []*ResourceScope{rcmgr.system.ResourceScope}),
		peer:          p,
		rcmgr:         rcmgr,
		system:        rcmgr.system,
		transient:     rcmgr.transient,
	}
}

func NewConnectionScope(dir network.Direction, usefd bool, limit Limit, rcmgr *ResourceManager) *ConnectionScope {
	return &ConnectionScope{
		ResourceScope: NewResourceScope(limit, []*ResourceScope{rcmgr.transient.ResourceScope, rcmgr.system.ResourceScope}),
		dir:           dir,
		usefd:         usefd,
		rcmgr:         rcmgr,
		system:        rcmgr.system,
		transient:     rcmgr.transient,
	}
}

func NewStreamScope(dir network.Direction, limit Limit, peer *PeerScope) *StreamScope {
	return &StreamScope{
		ResourceScope: NewResourceScope(limit, []*ResourceScope{peer.ResourceScope, peer.transient.ResourceScope, peer.system.ResourceScope}),
		dir:           dir,
		rcmgr:         peer.rcmgr,
		system:        peer.system,
		transient:     peer.transient,
		peer:          peer,
	}
}

func (s *ServiceScope) Name() string {
	return s.name
}

func (s *ProtocolScope) Protocol() protocol.ID {
	return s.proto
}

func (s *PeerScope) Peer() peer.ID {
	return s.peer
}

func (s *PeerScope) OpenStream(dir network.Direction) (network.StreamScope, error) {
	stream := NewStreamScope(dir, s.rcmgr.getStreamLimit(s.peer), s)
	err := stream.AddStream(dir)
	if err != nil {
		return nil, err
	}

	return stream, nil
}

func (s *ConnectionScope) PeerScope() network.PeerScope {
	s.Lock()
	defer s.Unlock()
	return s.peer
}

func (s *ConnectionScope) SetPeer(p peer.ID) error {
	s.Lock()
	defer s.Unlock()

	if s.peer != nil {
		return fmt.Errorf("connection scope already attached to a peer")
	}
	s.peer = s.rcmgr.getPeerScope(p)

	// juggle resources from transient scope to peer scope
	mem := s.ResourceScope.rc.memory

	var incount, outcount int
	if s.dir == network.DirInbound {
		incount = 1
	} else {
		outcount = 1
	}

	if err := s.peer.ReserveMemoryForChild(mem); err != nil {
		return err
	}
	if err := s.peer.AddConnForChild(incount, outcount); err != nil {
		s.peer.ReleaseMemoryForChild(mem)
		return err
	}
	if s.usefd {
		if err := s.peer.AddFDForChild(1); err != nil {
			s.peer.ReleaseMemoryForChild(mem)
			s.peer.RemoveConnForChild(incount, outcount)
			return err
		}
	}

	s.transient.ReleaseMemoryForChild(mem)
	s.transient.RemoveConnForChild(incount, outcount)
	if s.usefd {
		s.transient.RemoveFDForChild(1)
	}

	// update constraints
	constraints := []*ResourceScope{
		s.peer.ResourceScope,
		s.system.ResourceScope,
	}
	s.ResourceScope.constraints = constraints

	return nil
}

func (s *StreamScope) ProtocolScope() network.ProtocolScope {
	s.Lock()
	defer s.Unlock()
	return s.proto
}

func (s *StreamScope) SetProtocol(proto protocol.ID) error {
	s.Lock()
	defer s.Unlock()

	if s.proto != nil {
		return fmt.Errorf("stream scope already attached to a protocol")
	}

	s.proto = s.rcmgr.getProtocolScope(proto)

	// juggle resources from transient scope to protocol scope
	mem := s.ResourceScope.rc.memory

	var incount, outcount int
	if s.dir == network.DirInbound {
		incount = 1
	} else {
		outcount = 1
	}

	if err := s.proto.ReserveMemoryForChild(mem); err != nil {
		return err
	}
	if err := s.proto.AddStreamForChild(incount, outcount); err != nil {
		s.proto.ReleaseMemoryForChild(mem)
		return err
	}

	s.transient.ReleaseMemoryForChild(mem)
	s.transient.RemoveStreamForChild(incount, outcount)

	// update constraints
	constraints := []*ResourceScope{
		s.peer.ResourceScope,
		s.proto.ResourceScope,
		s.system.ResourceScope,
	}
	s.ResourceScope.constraints = constraints

	return nil
}

func (s *StreamScope) ServiceScope() network.ServiceScope {
	s.Lock()
	defer s.Unlock()
	return s.svc
}

func (s *StreamScope) SetService(svc string) error {
	s.Lock()
	defer s.Unlock()

	if s.proto == nil {
		return fmt.Errorf("stream scope not attached to a protocol")
	}
	if s.svc != nil {
		return fmt.Errorf("stream scope already attached to a service")
	}

	s.svc = s.rcmgr.getServiceScope(svc)

	// reserve resources in service
	mem := s.ResourceScope.rc.memory

	var incount, outcount int
	if s.dir == network.DirInbound {
		incount = 1
	} else {
		outcount = 1
	}

	if err := s.svc.ReserveMemoryForChild(mem); err != nil {
		return err
	}
	if err := s.svc.AddStreamForChild(incount, outcount); err != nil {
		s.svc.ReleaseMemoryForChild(mem)
		return err
	}

	// update constraints
	constraints := []*ResourceScope{
		s.peer.ResourceScope,
		s.proto.ResourceScope,
		s.svc.ResourceScope,
		s.system.ResourceScope,
	}
	s.ResourceScope.constraints = constraints

	return nil
}

func (s *StreamScope) PeerScope() network.PeerScope {
	s.Lock()
	defer s.Unlock()
	return s.peer
}
<|endoftext|>


<commit_before>package state

import (
	"bufio"
	"errors"
	"fmt"
	"io"
	"net/http"
	"os"
	"path"
	"strconv"
	"strings"
	"time"

	"github.com/omniscale/imposm3/config"
	"github.com/omniscale/imposm3/logging"
	"github.com/omniscale/imposm3/parser/pbf"
)

var log = logging.NewLogger("diff")

type DiffState struct {
	Time     time.Time
	Sequence int
	Url      string
}

func (d DiffState) String() string {
	return fmt.Sprintf("Diff #%d from %s", d.Sequence, d.Time.Local())
}

func (d DiffState) Write(w io.Writer) error {
	lines := []string{}
	lines = append(lines, "timestamp="+d.Time.Format(timestampFormat))
	if d.Sequence != 0 {
		lines = append(lines, "sequenceNumber="+fmt.Sprintf("%d", d.Sequence))
	}
	lines = append(lines, "replicationUrl="+d.Url)

	for _, line := range lines {
		_, err := w.Write([]byte(line + "\n"))
		if err != nil {
			return err
		}
	}
	return nil
}

func WriteLastState(cacheDir string, state *DiffState) error {
	stateFile := path.Join(cacheDir, "last.state.txt")
	f, err := os.Create(stateFile)
	if err != nil {
		return err
	}
	defer f.Close()
	return state.Write(f)
}

func FromOscGz(oscFile string) (*DiffState, error) {
	var stateFile string
	if !strings.HasSuffix(oscFile, ".osc.gz") {
		log.Warn("cannot read state file for non .osc.gz files")
		return nil, nil
	}

	stateFile = oscFile[:len(oscFile)-len(".osc.gz")] + ".state.txt"
	if _, err := os.Stat(stateFile); os.IsNotExist(err) {
		log.Warn("cannot find state file ", stateFile)
		return nil, nil
	}

	f, err := os.Open(stateFile)
	if err != nil {
		return nil, err
	}
	defer f.Close()
	return ParseFile(stateFile)
}

func FromPbf(filename string, before time.Duration) (*DiffState, error) {
	pbfFile, err := pbf.NewParser(filename)
	if err != nil {
		return nil, err
	}
	var timestamp time.Time
	if pbfFile.Header().Time.Unix() != 0 {
		timestamp = pbfFile.Header().Time
	} else {
		fstat, err := os.Stat(filename)
		if err != nil {
			return nil, err
		}
		timestamp = fstat.ModTime()
	}

	replicationUrl := config.BaseOptions.ReplicationUrl
	if replicationUrl == "" {
		replicationUrl = "https://planet.openstreetmap.org/replication/minute/"
	}

	seq := estimateSequence(replicationUrl, timestamp)
	if seq == 0 {
		return nil, nil
	}

	// start earlier
	seq -= int(before.Minutes())
	return &DiffState{Time: timestamp, Url: replicationUrl, Sequence: seq}, nil
}

func ParseFile(stateFile string) (*DiffState, error) {
	f, err := os.Open(stateFile)
	if err != nil {
		return nil, err
	}
	defer f.Close()
	return Parse(f)
}

func Parse(f io.Reader) (*DiffState, error) {
	values, err := parseSimpleIni(f)
	if err != nil {
		return nil, err
	}

	timestamp, err := parseTimeStamp(values["timestamp"])
	if err != nil {
		return nil, err
	}
	sequence, err := parseSequence(values["sequenceNumber"])
	if err != nil {
		return nil, err
	}

	url := values["replicationUrl"]
	return &DiffState{timestamp, sequence, url}, nil
}

func ParseLastState(cacheDir string) (*DiffState, error) {
	stateFile := path.Join(cacheDir, "last.state.txt")
	if _, err := os.Stat(stateFile); os.IsNotExist(err) {
		return nil, err
	}
	return ParseFile(stateFile)
}

func parseSimpleIni(f io.Reader) (map[string]string, error) {
	result := make(map[string]string)

	reader := bufio.NewScanner(f)
	for reader.Scan() {
		line := reader.Text()
		if line != "" && line[0] == '#' {
			continue
		}
		if strings.Contains(line, "=") {
			keyVal := strings.SplitN(line, "=", 2)
			result[strings.TrimSpace(keyVal[0])] = strings.TrimSpace(keyVal[1])
		}

	}
	if err := reader.Err(); err != nil {
		return nil, err
	}

	return result, nil
}

const timestampFormat = "2006-01-02T15\\:04\\:05Z"

func parseTimeStamp(value string) (time.Time, error) {
	if value == "" {
		return time.Time{}, errors.New("missing timestamp in state")
	}
	return time.Parse(timestampFormat, value)
}

func parseSequence(value string) (int, error) {
	if value == "" {
		log.Warn("missing sequenceNumber in state file")
		return 0, nil
	}
	val, err := strconv.ParseInt(value, 10, 32)
	return int(val), err
}

func currentState(url string) (*DiffState, error) {
	resp, err := http.Get(url + "state.txt")
	if err != nil {
		return nil, err
	}
	if resp.StatusCode != 200 {
		return nil, errors.New(fmt.Sprintf("invalid response: %v", resp))
	}
	defer resp.Body.Close()
	return Parse(resp.Body)
}

func estimateSequence(url string, timestamp time.Time) int {
	state, err := currentState(url)
	if err != nil {
		// try a second time before failing
		log.Warn("unable to fetch current state from ", url, ":", err, ", retry in 30s")
		time.Sleep(time.Second * 30)
		state, err = currentState(url)
		if err != nil {
			log.Warn("unable to fetch current state from ", url, ":", err, ", giving up")
			return 0
		}
	}

	behind := state.Time.Sub(timestamp)
	return state.Sequence - int(behind.Minutes())
}
<commit_msg>Adapt estimateSequence to replication interval<commit_after>package state

import (
	"bufio"
	"errors"
	"fmt"
	"io"
	"math"
	"net/http"
	"os"
	"path"
	"strconv"
	"strings"
	"time"

	"github.com/omniscale/imposm3/config"
	"github.com/omniscale/imposm3/logging"
	"github.com/omniscale/imposm3/parser/pbf"
)

var log = logging.NewLogger("diff")

type DiffState struct {
	Time     time.Time
	Sequence int
	Url      string
}

func (d DiffState) String() string {
	return fmt.Sprintf("Diff #%d from %s", d.Sequence, d.Time.Local())
}

func (d DiffState) Write(w io.Writer) error {
	lines := []string{}
	lines = append(lines, "timestamp="+d.Time.Format(timestampFormat))
	if d.Sequence != 0 {
		lines = append(lines, "sequenceNumber="+fmt.Sprintf("%d", d.Sequence))
	}
	lines = append(lines, "replicationUrl="+d.Url)

	for _, line := range lines {
		_, err := w.Write([]byte(line + "\n"))
		if err != nil {
			return err
		}
	}
	return nil
}

func WriteLastState(cacheDir string, state *DiffState) error {
	stateFile := path.Join(cacheDir, "last.state.txt")
	f, err := os.Create(stateFile)
	if err != nil {
		return err
	}
	defer f.Close()
	return state.Write(f)
}

func FromOscGz(oscFile string) (*DiffState, error) {
	var stateFile string
	if !strings.HasSuffix(oscFile, ".osc.gz") {
		log.Warn("cannot read state file for non .osc.gz files")
		return nil, nil
	}

	stateFile = oscFile[:len(oscFile)-len(".osc.gz")] + ".state.txt"
	if _, err := os.Stat(stateFile); os.IsNotExist(err) {
		log.Warn("cannot find state file ", stateFile)
		return nil, nil
	}

	f, err := os.Open(stateFile)
	if err != nil {
		return nil, err
	}
	defer f.Close()
	return ParseFile(stateFile)
}

func FromPbf(filename string, before time.Duration) (*DiffState, error) {
	pbfFile, err := pbf.NewParser(filename)
	if err != nil {
		return nil, err
	}
	var timestamp time.Time
	if pbfFile.Header().Time.Unix() != 0 {
		timestamp = pbfFile.Header().Time
	} else {
		fstat, err := os.Stat(filename)
		if err != nil {
			return nil, err
		}
		timestamp = fstat.ModTime()
	}

	replicationUrl := config.BaseOptions.ReplicationUrl
	if replicationUrl == "" {
		replicationUrl = "https://planet.openstreetmap.org/replication/minute/"
	}

	seq := estimateSequence(replicationUrl, timestamp)
	if seq == 0 {
		return nil, nil
	}

	// start earlier
	seq -= int(before.Minutes())
	return &DiffState{Time: timestamp, Url: replicationUrl, Sequence: seq}, nil
}

func ParseFile(stateFile string) (*DiffState, error) {
	f, err := os.Open(stateFile)
	if err != nil {
		return nil, err
	}
	defer f.Close()
	return Parse(f)
}

func Parse(f io.Reader) (*DiffState, error) {
	values, err := parseSimpleIni(f)
	if err != nil {
		return nil, err
	}

	timestamp, err := parseTimeStamp(values["timestamp"])
	if err != nil {
		return nil, err
	}
	sequence, err := parseSequence(values["sequenceNumber"])
	if err != nil {
		return nil, err
	}

	url := values["replicationUrl"]
	return &DiffState{timestamp, sequence, url}, nil
}

func ParseLastState(cacheDir string) (*DiffState, error) {
	stateFile := path.Join(cacheDir, "last.state.txt")
	if _, err := os.Stat(stateFile); os.IsNotExist(err) {
		return nil, err
	}
	return ParseFile(stateFile)
}

func parseSimpleIni(f io.Reader) (map[string]string, error) {
	result := make(map[string]string)

	reader := bufio.NewScanner(f)
	for reader.Scan() {
		line := reader.Text()
		if line != "" && line[0] == '#' {
			continue
		}
		if strings.Contains(line, "=") {
			keyVal := strings.SplitN(line, "=", 2)
			result[strings.TrimSpace(keyVal[0])] = strings.TrimSpace(keyVal[1])
		}

	}
	if err := reader.Err(); err != nil {
		return nil, err
	}

	return result, nil
}

const timestampFormat = "2006-01-02T15\\:04\\:05Z"

func parseTimeStamp(value string) (time.Time, error) {
	if value == "" {
		return time.Time{}, errors.New("missing timestamp in state")
	}
	return time.Parse(timestampFormat, value)
}

func parseSequence(value string) (int, error) {
	if value == "" {
		log.Warn("missing sequenceNumber in state file")
		return 0, nil
	}
	val, err := strconv.ParseInt(value, 10, 32)
	return int(val), err
}

func currentState(url string) (*DiffState, error) {
	resp, err := http.Get(url + "state.txt")
	if err != nil {
		return nil, err
	}
	if resp.StatusCode != 200 {
		return nil, errors.New(fmt.Sprintf("invalid response: %v", resp))
	}
	defer resp.Body.Close()
	return Parse(resp.Body)
}

func estimateSequence(url string, timestamp time.Time) int {
	state, err := currentState(url)
	if err != nil {
		// try a second time before failing
		log.Warn("unable to fetch current state from ", url, ":", err, ", retry in 30s")
		time.Sleep(time.Second * 30)
		state, err = currentState(url)
		if err != nil {
			log.Warn("unable to fetch current state from ", url, ":", err, ", giving up")
			return 0
		}
	}

    behind := state.Time.Sub(timestamp)
    // Sequence unit depends on replication interval (minute, hour, day).
    return state.Sequence - int(math.Ceil(behind.Minutes() / config.BaseOptions.ReplicationInterval.Minutes()))
}
<|endoftext|>


<commit_before>package libvirt

import (
	"os"

	"github.com/hashicorp/terraform/helper/schema"
	libvirt "github.com/libvirt/libvirt-go"
	libvirtxml "github.com/libvirt/libvirt-go-xml"
)

// note source and target are not initialized
func newFilesystemDef() libvirtxml.DomainFilesystem {
	return libvirtxml.DomainFilesystem{
		AccessMode: "mapped", // A safe default value
		ReadOnly:   &libvirtxml.DomainFilesystemReadOnly{},
	}
}

// Creates a domain definition with the defaults
// the provider uses
func newDomainDef() libvirtxml.Domain {
	domainDef := libvirtxml.Domain{
		OS: &libvirtxml.DomainOS{
			Type: &libvirtxml.DomainOSType{
				Type: "hvm",
			},
		},
		Memory: &libvirtxml.DomainMemory{
			Unit:  "MiB",
			Value: 512,
		},
		VCPU: &libvirtxml.DomainVCPU{
			Placement: "static",
			Value:     1,
		},
		CPU: &libvirtxml.DomainCPU{},
		Devices: &libvirtxml.DomainDeviceList{
			Graphics: []libvirtxml.DomainGraphic{
				{
					Spice: &libvirtxml.DomainGraphicSpice{
						AutoPort: "yes",
					},
				},
			},
			Channels: []libvirtxml.DomainChannel{
				{
					Target: &libvirtxml.DomainChannelTarget{
						VirtIO: &libvirtxml.DomainChannelTargetVirtIO{
							Name: "org.qemu.guest_agent.0",
						},
					},
				},
			},
			RNGs: []libvirtxml.DomainRNG{
				{
					Model: "virtio",
					Backend: &libvirtxml.DomainRNGBackend{
						Random: &libvirtxml.DomainRNGBackendRandom{},
					},
				},
			},
		},
		Features: &libvirtxml.DomainFeatureList{
			PAE:  &libvirtxml.DomainFeature{},
			ACPI: &libvirtxml.DomainFeature{},
			APIC: &libvirtxml.DomainFeatureAPIC{},
		},
	}

	if v := os.Getenv("TERRAFORM_LIBVIRT_TEST_DOMAIN_TYPE"); v != "" {
		domainDef.Type = v
	} else {
		domainDef.Type = "kvm"
	}

	return domainDef
}

func newDomainDefForConnection(virConn *libvirt.Connect, rd *schema.ResourceData) (libvirtxml.Domain, error) {
	d := newDomainDef()

	if arch, ok := rd.GetOk("arch"); ok {
		d.OS.Type.Arch = arch.(string)
	} else {
		arch, err := getHostArchitecture(virConn)
		if err != nil {
			return d, err
		}
		d.OS.Type.Arch = arch
	}

	caps, err := getHostCapabilities(virConn)
	if err != nil {
		return d, err
	}
	guest, err := getGuestForArchType(caps, d.OS.Type.Arch, d.OS.Type.Type)
	if err != nil {
		return d, err
	}

	if emulator, ok := rd.GetOk("emulator"); ok {
		d.Devices.Emulator = emulator.(string)
	} else {
		d.Devices.Emulator = guest.Arch.Emulator
	}

	if machine, ok := rd.GetOk("machine"); ok {
		d.OS.Type.Machine = machine.(string)
	} else if len(guest.Arch.Machines) > 0 {
		d.OS.Type.Machine = guest.Arch.Machines[0].Name
	}

	canonicalmachine, err := getCanonicalMachineName(caps, d.OS.Type.Arch, d.OS.Type.Type, d.OS.Type.Machine)
	if err != nil {
		return d, err
	}
	d.OS.Type.Machine = canonicalmachine
	return d, nil
}
<commit_msg>domain refactoring: new function<commit_after>package libvirt

import (
	"encoding/xml"
	"fmt"
	"os"

	"github.com/hashicorp/terraform/helper/schema"
	libvirt "github.com/libvirt/libvirt-go"
	libvirtxml "github.com/libvirt/libvirt-go-xml"
)

// from existing domain return its  XMLdefintion
func getXMLDomainDefFromLibvirt(domain *libvirt.Domain) (libvirtxml.Domain, error) {
	domainXMLDesc, err := domain.GetXMLDesc(0)
	if err != nil {
		return libvirtxml.Domain{}, fmt.Errorf("Error retrieving libvirt domain XML description: %s", err)
	}

	domainDef := newDomainDef()
	err = xml.Unmarshal([]byte(domainXMLDesc), &domainDef)
	if err != nil {
		return libvirtxml.Domain{}, fmt.Errorf("Error reading libvirt domain XML description: %s", err)
	}

	return domainDef, nil
}

// note source and target are not initialized
func newFilesystemDef() libvirtxml.DomainFilesystem {
	return libvirtxml.DomainFilesystem{
		AccessMode: "mapped", // A safe default value
		ReadOnly:   &libvirtxml.DomainFilesystemReadOnly{},
	}
}

// Creates a domain definition with the defaults
// the provider uses
func newDomainDef() libvirtxml.Domain {
	domainDef := libvirtxml.Domain{
		OS: &libvirtxml.DomainOS{
			Type: &libvirtxml.DomainOSType{
				Type: "hvm",
			},
		},
		Memory: &libvirtxml.DomainMemory{
			Unit:  "MiB",
			Value: 512,
		},
		VCPU: &libvirtxml.DomainVCPU{
			Placement: "static",
			Value:     1,
		},
		CPU: &libvirtxml.DomainCPU{},
		Devices: &libvirtxml.DomainDeviceList{
			Graphics: []libvirtxml.DomainGraphic{
				{
					Spice: &libvirtxml.DomainGraphicSpice{
						AutoPort: "yes",
					},
				},
			},
			Channels: []libvirtxml.DomainChannel{
				{
					Target: &libvirtxml.DomainChannelTarget{
						VirtIO: &libvirtxml.DomainChannelTargetVirtIO{
							Name: "org.qemu.guest_agent.0",
						},
					},
				},
			},
			RNGs: []libvirtxml.DomainRNG{
				{
					Model: "virtio",
					Backend: &libvirtxml.DomainRNGBackend{
						Random: &libvirtxml.DomainRNGBackendRandom{},
					},
				},
			},
		},
		Features: &libvirtxml.DomainFeatureList{
			PAE:  &libvirtxml.DomainFeature{},
			ACPI: &libvirtxml.DomainFeature{},
			APIC: &libvirtxml.DomainFeatureAPIC{},
		},
	}

	if v := os.Getenv("TERRAFORM_LIBVIRT_TEST_DOMAIN_TYPE"); v != "" {
		domainDef.Type = v
	} else {
		domainDef.Type = "kvm"
	}

	return domainDef
}

func newDomainDefForConnection(virConn *libvirt.Connect, rd *schema.ResourceData) (libvirtxml.Domain, error) {
	d := newDomainDef()

	if arch, ok := rd.GetOk("arch"); ok {
		d.OS.Type.Arch = arch.(string)
	} else {
		arch, err := getHostArchitecture(virConn)
		if err != nil {
			return d, err
		}
		d.OS.Type.Arch = arch
	}

	caps, err := getHostCapabilities(virConn)
	if err != nil {
		return d, err
	}
	guest, err := getGuestForArchType(caps, d.OS.Type.Arch, d.OS.Type.Type)
	if err != nil {
		return d, err
	}

	if emulator, ok := rd.GetOk("emulator"); ok {
		d.Devices.Emulator = emulator.(string)
	} else {
		d.Devices.Emulator = guest.Arch.Emulator
	}

	if machine, ok := rd.GetOk("machine"); ok {
		d.OS.Type.Machine = machine.(string)
	} else if len(guest.Arch.Machines) > 0 {
		d.OS.Type.Machine = guest.Arch.Machines[0].Name
	}

	canonicalmachine, err := getCanonicalMachineName(caps, d.OS.Type.Arch, d.OS.Type.Type, d.OS.Type.Machine)
	if err != nil {
		return d, err
	}
	d.OS.Type.Machine = canonicalmachine
	return d, nil
}
<|endoftext|>


<commit_before>package listeners

import (
	"net"
	"net/http"
	"sync"
	"time"

	"github.com/getlantern/measured"
)

const (
	rateInterval = 1 * time.Second
)

// MeasuredReportFN is a function that gets called to report stats from the
// measured connection. deltaStats is like stats except that SentTotal and
// RecvTotal are deltas relative to the prior reported stats. final indicates
// whether this is the last call for a connection (i.e. connection has been
// closed).
type MeasuredReportFN func(ctx map[string]interface{}, stats *measured.Stats, deltaStats *measured.Stats,
	final bool)

// Wrapped stateAwareMeasuredListener that generates the wrapped wrapMeasuredConn
type stateAwareMeasuredListener struct {
	net.Listener
	reportInterval time.Duration
	report         MeasuredReportFN
}

func NewMeasuredListener(l net.Listener, reportInterval time.Duration, report MeasuredReportFN) net.Listener {
	return &stateAwareMeasuredListener{l, reportInterval, report}
}

func (l *stateAwareMeasuredListener) Accept() (c net.Conn, err error) {
	c, err = l.Listener.Accept()
	if err != nil {
		return nil, err
	}
	fs := make(chan *measured.Stats)
	wc := &wrapMeasuredConn{
		Conn: measured.Wrap(c, rateInterval, func(mc measured.Conn) {
			fs <- mc.Stats()
		}),
		ctx:        make(map[string]interface{}),
		finalStats: fs,
	}
	sac, _ := c.(WrapConnEmbeddable)
	wc.WrapConnEmbeddable = sac
	go wc.track(l.reportInterval, l.report)
	return wc, nil
}

// Wrapped MeasuredConn that supports OnState
type wrapMeasuredConn struct {
	WrapConnEmbeddable
	measured.Conn
	ctx        map[string]interface{}
	ctxMx      sync.RWMutex
	finalStats chan *measured.Stats
}

func (c *wrapMeasuredConn) track(reportInterval time.Duration, report MeasuredReportFN) {
	ticker := time.NewTicker(reportInterval)
	var priorStats *measured.Stats
	applyStats := func(stats *measured.Stats, final bool) {
		deltaStats := stats
		if priorStats != nil {
			deltaStats.SentTotal -= priorStats.SentTotal
			deltaStats.RecvTotal -= priorStats.RecvTotal
		}
		priorStats = stats
		c.ctxMx.RLock()
		ctx := c.ctx
		c.ctxMx.RUnlock()
		report(ctx, stats, deltaStats, final)
	}

	for {
		select {
		case <-ticker.C:
			applyStats(c.Conn.Stats(), false)
		case stats := <-c.finalStats:
			applyStats(stats, true)
			return
		}
	}
}

func (c *wrapMeasuredConn) OnState(s http.ConnState) {
	if c.WrapConnEmbeddable != nil {
		c.WrapConnEmbeddable.OnState(s)
	}
}

// Responds to the "measured" message type
func (c *wrapMeasuredConn) ControlMessage(msgType string, data interface{}) {
	if msgType == "measured" {
		ctxUpdate := data.(map[string]interface{})
		c.ctxMx.Lock()
		newContext := make(map[string]interface{}, len(c.ctx))
		// Copy context
		for key, value := range c.ctx {
			newContext[key] = value
		}
		// Update context
		for key, value := range ctxUpdate {
			newContext[key] = value
		}
		c.ctx = newContext
		c.ctxMx.Unlock()
	}

	if c.WrapConnEmbeddable != nil {
		// Pass it down too, just in case other wrapper does something with
		c.WrapConnEmbeddable.ControlMessage(msgType, data)
	}
}
<commit_msg>Fixed measured delta calculation<commit_after>package listeners

import (
	"net"
	"net/http"
	"sync"
	"time"

	"github.com/getlantern/measured"
)

const (
	rateInterval = 1 * time.Second
)

// MeasuredReportFN is a function that gets called to report stats from the
// measured connection. deltaStats is like stats except that SentTotal and
// RecvTotal are deltas relative to the prior reported stats. final indicates
// whether this is the last call for a connection (i.e. connection has been
// closed).
type MeasuredReportFN func(ctx map[string]interface{}, stats *measured.Stats, deltaStats *measured.Stats,
	final bool)

// Wrapped stateAwareMeasuredListener that generates the wrapped wrapMeasuredConn
type stateAwareMeasuredListener struct {
	net.Listener
	reportInterval time.Duration
	report         MeasuredReportFN
}

func NewMeasuredListener(l net.Listener, reportInterval time.Duration, report MeasuredReportFN) net.Listener {
	return &stateAwareMeasuredListener{l, reportInterval, report}
}

func (l *stateAwareMeasuredListener) Accept() (c net.Conn, err error) {
	c, err = l.Listener.Accept()
	if err != nil {
		return nil, err
	}
	fs := make(chan *measured.Stats)
	wc := &wrapMeasuredConn{
		Conn: measured.Wrap(c, rateInterval, func(mc measured.Conn) {
			fs <- mc.Stats()
		}),
		ctx:        make(map[string]interface{}),
		finalStats: fs,
	}
	sac, _ := c.(WrapConnEmbeddable)
	wc.WrapConnEmbeddable = sac
	go wc.track(l.reportInterval, l.report)
	return wc, nil
}

// Wrapped MeasuredConn that supports OnState
type wrapMeasuredConn struct {
	WrapConnEmbeddable
	measured.Conn
	ctx        map[string]interface{}
	ctxMx      sync.RWMutex
	finalStats chan *measured.Stats
}

func (c *wrapMeasuredConn) track(reportInterval time.Duration, report MeasuredReportFN) {
	ticker := time.NewTicker(reportInterval)
	var priorStats *measured.Stats
	applyStats := func(stats *measured.Stats, final bool) {
		deltaStats := &measured.Stats{}
		// Copy stats
		*deltaStats = *stats
		if priorStats != nil {
			deltaStats.SentTotal -= priorStats.SentTotal
			deltaStats.RecvTotal -= priorStats.RecvTotal
		}
		priorStats = stats
		c.ctxMx.RLock()
		ctx := c.ctx
		c.ctxMx.RUnlock()
		report(ctx, stats, deltaStats, final)
	}

	for {
		select {
		case <-ticker.C:
			applyStats(c.Conn.Stats(), false)
		case stats := <-c.finalStats:
			applyStats(stats, true)
			return
		}
	}
}

func (c *wrapMeasuredConn) OnState(s http.ConnState) {
	if c.WrapConnEmbeddable != nil {
		c.WrapConnEmbeddable.OnState(s)
	}
}

// Responds to the "measured" message type
func (c *wrapMeasuredConn) ControlMessage(msgType string, data interface{}) {
	if msgType == "measured" {
		ctxUpdate := data.(map[string]interface{})
		c.ctxMx.Lock()
		newContext := make(map[string]interface{}, len(c.ctx))
		// Copy context
		for key, value := range c.ctx {
			newContext[key] = value
		}
		// Update context
		for key, value := range ctxUpdate {
			newContext[key] = value
		}
		c.ctx = newContext
		c.ctxMx.Unlock()
	}

	if c.WrapConnEmbeddable != nil {
		// Pass it down too, just in case other wrapper does something with
		c.WrapConnEmbeddable.ControlMessage(msgType, data)
	}
}
<|endoftext|>


<commit_before>package lnwallet

import (
	"errors"
	"fmt"
	"sync"

	"github.com/roasbeef/btcd/btcec"
	"github.com/roasbeef/btcd/chaincfg/chainhash"
	"github.com/roasbeef/btcd/wire"
	"github.com/roasbeef/btcutil"
)

// ErrNotMine is an error denoting that a WalletController instance is unable
// to spend a specified output.
var ErrNotMine = errors.New("the passed output doesn't belong to the wallet")

// AddressType is a enum-like type which denotes the possible address types
// WalletController supports.
type AddressType uint8

const (
	// UnknownAddressType represents an output with an unknown or non-standard
	// script.
	UnknownAddressType AddressType = iota

	// WitnessPubKey represents a p2wkh address.
	WitnessPubKey

	// NestedWitnessPubKey represents a p2sh output which is itself a
	// nested p2wkh output.
	NestedWitnessPubKey

	// PubKeyHash represents a regular p2pkh output.
	PubKeyHash
)

// ErrDoubleSpend is returned from PublishTransaction in case the
// tx being published is spending an output spent by a conflicting
// transaction.
var ErrDoubleSpend = errors.New("Transaction rejected: output already spent")

// Utxo is an unspent output denoted by its outpoint, and output value of the
// original output.
type Utxo struct {
	AddressType   AddressType
	Value         btcutil.Amount
	PkScript      []byte
	RedeemScript  []byte
	WitnessScript []byte
	wire.OutPoint
}

// TransactionDetail describes a transaction with either inputs which belong to
// the wallet, or has outputs that pay to the wallet.
type TransactionDetail struct {
	// Hash is the transaction hash of the transaction.
	Hash chainhash.Hash

	// Value is the net value of this transaction (in satoshis) from the
	// PoV of the wallet. If this transaction purely spends from the
	// wallet's funds, then this value will be negative. Similarly, if this
	// transaction credits the wallet, then this value will be positive.
	Value btcutil.Amount

	// NumConfirmations is the number of confirmations this transaction
	// has. If the transaction is unconfirmed, then this value will be
	// zero.
	NumConfirmations int32

	// BlockHeight is the hash of the block which includes this
	// transaction. Unconfirmed transactions will have a nil value for this
	// field.
	BlockHash *chainhash.Hash

	// BlockHeight is the height of the block including this transaction.
	// Unconfirmed transaction will show a height of zero.
	BlockHeight int32

	// Timestamp is the unix timestamp of the block including this
	// transaction. If the transaction is unconfirmed, then this will be a
	// timestamp of txn creation.
	Timestamp int64

	// TotalFees is the total fee in satoshis paid by this transaction.
	TotalFees int64

	// DestAddresses are the destinations for a transaction
	DestAddresses []btcutil.Address
}

// TransactionSubscription is an interface which describes an object capable of
// receiving notifications of new transaction related to the underlying wallet.
// TODO(roasbeef): add balance updates?
type TransactionSubscription interface {
	// ConfirmedTransactions returns a channel which will be sent on as new
	// relevant transactions are confirmed.
	ConfirmedTransactions() chan *TransactionDetail

	// UnconfirmedTransactions returns a channel which will be sent on as
	// new relevant transactions are seen within the network.
	UnconfirmedTransactions() chan *TransactionDetail

	// Cancel finalizes the subscription, cleaning up any resources
	// allocated.
	Cancel()
}

// WalletController defines an abstract interface for controlling a local Pure
// Go wallet, a local or remote wallet via an RPC mechanism, or possibly even
// a daemon assisted hardware wallet. This interface serves the purpose of
// allowing LightningWallet to be seamlessly compatible with several wallets
// such as: uspv, btcwallet, Bitcoin Core, Electrum, etc. This interface then
// serves as a "base wallet", with Lightning Network awareness taking place at
// a "higher" level of abstraction. Essentially, an overlay wallet.
// Implementors of this interface must closely adhere to the documented
// behavior of all interface methods in order to ensure identical behavior
// across all concrete implementations.
type WalletController interface {
	// FetchInputInfo queries for the WalletController's knowledge of the
	// passed outpoint. If the base wallet determines this output is under
	// its control, then the original txout should be returned. Otherwise,
	// a non-nil error value of ErrNotMine should be returned instead.
	FetchInputInfo(prevOut *wire.OutPoint) (*wire.TxOut, error)

	// ConfirmedBalance returns the sum of all the wallet's unspent outputs
	// that have at least confs confirmations. If confs is set to zero,
	// then all unspent outputs, including those currently in the mempool
	// will be included in the final sum.
	ConfirmedBalance(confs int32, witness bool) (btcutil.Amount, error)

	// NewAddress returns the next external or internal address for the
	// wallet dictated by the value of the `change` parameter. If change is
	// true, then an internal address should be used, otherwise an external
	// address should be returned. The type of address returned is dictated
	// by the wallet's capabilities, and may be of type: p2sh, p2pkh,
	// p2wkh, p2wsh, etc.
	NewAddress(addrType AddressType, change bool) (btcutil.Address, error)

	// GetPrivKey retrieves the underlying private key associated with the
	// passed address. If the wallet is unable to locate this private key
	// due to the address not being under control of the wallet, then an
	// error should be returned.
	GetPrivKey(a btcutil.Address) (*btcec.PrivateKey, error)

	// NewRawKey returns a raw private key controlled by the wallet. These
	// keys are used for the 2-of-2 multi-sig outputs for funding
	// transactions, as well as the pub key used for commitment transactions.
	//
	// NOTE: The wallet MUST watch for on-chain outputs created to a p2wpkh
	// script using keys returned by this function.
	NewRawKey() (*btcec.PublicKey, error)

	// FetchRootKey returns a root key which will be used by the
	// LightningWallet to deterministically generate secrets. The private
	// key returned by this method should remain constant in-between
	// WalletController restarts.
	FetchRootKey() (*btcec.PrivateKey, error)

	// SendOutputs funds, signs, and broadcasts a Bitcoin transaction
	// paying out to the specified outputs. In the case the wallet has
	// insufficient funds, or the outputs are non-standard, an error should
	// be returned. This method also takes the target fee expressed in
	// sat/vbyte that should be used when crafting the transaction.
	SendOutputs(outputs []*wire.TxOut,
		feeRate SatPerVByte) (*chainhash.Hash, error)

	// ListUnspentWitness returns all unspent outputs which are version 0
	// witness programs. The 'confirms' parameter indicates the minimum
	// number of confirmations an output needs in order to be returned by
	// this method. Passing -1 as 'confirms' indicates that even
	// unconfirmed outputs should be returned.
	ListUnspentWitness(confirms int32) ([]*Utxo, error)

	// ListTransactionDetails returns a list of all transactions which are
	// relevant to the wallet.
	ListTransactionDetails() ([]*TransactionDetail, error)

	// LockOutpoint marks an outpoint as locked meaning it will no longer
	// be deemed as eligible for coin selection. Locking outputs are
	// utilized in order to avoid race conditions when selecting inputs for
	// usage when funding a channel.
	LockOutpoint(o wire.OutPoint)

	// UnlockOutpoint unlocks an previously locked output, marking it
	// eligible for coin selection.
	UnlockOutpoint(o wire.OutPoint)

	// PublishTransaction performs cursory validation (dust checks, etc),
	// then finally broadcasts the passed transaction to the Bitcoin network.
	// If the transaction is rejected because it is conflicting with an
	// already known transaction, ErrDoubleSpend is returned. If the
	// transaction is already known (published already), no error will be
	// returned. Other error returned depends on the currently active chain
	// backend.
	PublishTransaction(tx *wire.MsgTx) error

	// SubscribeTransactions returns a TransactionSubscription client which
	// is capable of receiving async notifications as new transactions
	// related to the wallet are seen within the network, or found in
	// blocks.
	//
	// NOTE: a non-nil error should be returned if notifications aren't
	// supported.
	//
	// TODO(roasbeef): make distinct interface?
	SubscribeTransactions() (TransactionSubscription, error)

	// IsSynced returns a boolean indicating if from the PoV of the wallet,
	// it has fully synced to the current best block in the main chain.
	// It also returns an int64 indicating the timestamp of the best block
	// known to the wallet, expressed in Unix epoch time
	IsSynced() (bool, int64, error)

	// Start initializes the wallet, making any necessary connections,
	// starting up required goroutines etc.
	Start() error

	// Stop signals the wallet for shutdown. Shutdown may entail closing
	// any active sockets, database handles, stopping goroutines, etc.
	Stop() error

	// BackEnd returns a name for the wallet's backing chain service,
	// which could be e.g. btcd, bitcoind, neutrino, or another consensus
	// service.
	BackEnd() string
}

// BlockChainIO is a dedicated source which will be used to obtain queries
// related to the current state of the blockchain. The data returned by each of
// the defined methods within this interface should always return the most up
// to date data possible.
//
// TODO(roasbeef): move to diff package perhaps?
// TODO(roasbeef): move publish txn here?
type BlockChainIO interface {
	// GetBestBlock returns the current height and block hash of the valid
	// most-work chain the implementation is aware of.
	GetBestBlock() (*chainhash.Hash, int32, error)

	// GetUtxo attempts to return the passed outpoint if it's still a
	// member of the utxo set. The passed height hint should be the "birth
	// height" of the passed outpoint. In the case that the output is in
	// the UTXO set, then the output corresponding to that output is
	// returned.  Otherwise, a non-nil error will be returned.
	GetUtxo(op *wire.OutPoint, heightHint uint32) (*wire.TxOut, error)

	// GetBlockHash returns the hash of the block in the best blockchain
	// at the given height.
	GetBlockHash(blockHeight int64) (*chainhash.Hash, error)

	// GetBlock returns the block in the main chain identified by the given
	// hash.
	GetBlock(blockHash *chainhash.Hash) (*wire.MsgBlock, error)
}

// Signer represents an abstract object capable of generating raw signatures as
// well as full complete input scripts given a valid SignDescriptor and
// transaction. This interface fully abstracts away signing paving the way for
// Signer implementations such as hardware wallets, hardware tokens, HSM's, or
// simply a regular wallet.
type Signer interface {
	// SignOutputRaw generates a signature for the passed transaction
	// according to the data within the passed SignDescriptor.
	//
	// NOTE: The resulting signature should be void of a sighash byte.
	SignOutputRaw(tx *wire.MsgTx, signDesc *SignDescriptor) ([]byte, error)

	// ComputeInputScript generates a complete InputIndex for the passed
	// transaction with the signature as defined within the passed
	// SignDescriptor. This method should be capable of generating the
	// proper input script for both regular p2wkh output and p2wkh outputs
	// nested within a regular p2sh output.
	//
	// NOTE: This method will ignore any tweak parameters set within the
	// passed SignDescriptor as it assumes a set of typical script
	// templates (p2wkh, np2wkh, etc).
	ComputeInputScript(tx *wire.MsgTx, signDesc *SignDescriptor) (*InputScript, error)
}

// MessageSigner represents an abstract object capable of signing arbitrary
// messages. The capabilities of this interface are used to sign announcements
// to the network, or just arbitrary messages that leverage the wallet's keys
// to attest to some message.
type MessageSigner interface {
	// SignMessage attempts to sign a target message with the private key
	// that corresponds to the passed public key. If the target private key
	// is unable to be found, then an error will be returned. The actual
	// digest signed is the double SHA-256 of the passed message.
	SignMessage(pubKey *btcec.PublicKey, msg []byte) (*btcec.Signature, error)
}

// PreimageCache is an interface that represents a global cache for preimages.
// We'll utilize this cache to communicate the discovery of new preimages
// across sub-systems.
type PreimageCache interface {
	// LookupPreimage attempts to look up a preimage according to its hash.
	// If found, the preimage is returned along with true for the second
	// argument. Otherwise, it'll return false.
	LookupPreimage(hash []byte) ([]byte, bool)

	// AddPreimage attempts to add a new preimage to the global cache. If
	// successful a nil error will be returned.
	AddPreimage(preimage []byte) error
}

// WalletDriver represents a "driver" for a particular concrete
// WalletController implementation. A driver is identified by a globally unique
// string identifier along with a 'New()' method which is responsible for
// initializing a particular WalletController concrete implementation.
type WalletDriver struct {
	// WalletType is a string which uniquely identifies the
	// WalletController that this driver, drives.
	WalletType string

	// New creates a new instance of a concrete WalletController
	// implementation given a variadic set up arguments. The function takes
	// a variadic number of interface parameters in order to provide
	// initialization flexibility, thereby accommodating several potential
	// WalletController implementations.
	New func(args ...interface{}) (WalletController, error)

	// BackEnds returns a list of available chain service drivers for the
	// wallet driver. This could be e.g. bitcoind, btcd, neutrino, etc.
	BackEnds func() []string
}

var (
	wallets     = make(map[string]*WalletDriver)
	registerMtx sync.Mutex
)

// RegisteredWallets returns a slice of all currently registered notifiers.
//
// NOTE: This function is safe for concurrent access.
func RegisteredWallets() []*WalletDriver {
	registerMtx.Lock()
	defer registerMtx.Unlock()

	registeredWallets := make([]*WalletDriver, 0, len(wallets))
	for _, wallet := range wallets {
		registeredWallets = append(registeredWallets, wallet)
	}

	return registeredWallets
}

// RegisterWallet registers a WalletDriver which is capable of driving a
// concrete WalletController interface. In the case that this driver has
// already been registered, an error is returned.
//
// NOTE: This function is safe for concurrent access.
func RegisterWallet(driver *WalletDriver) error {
	registerMtx.Lock()
	defer registerMtx.Unlock()

	if _, ok := wallets[driver.WalletType]; ok {
		return fmt.Errorf("wallet already registered")
	}

	wallets[driver.WalletType] = driver

	return nil
}

// SupportedWallets returns a slice of strings that represents the wallet
// drivers that have been registered and are therefore supported.
//
// NOTE: This function is safe for concurrent access.
func SupportedWallets() []string {
	registerMtx.Lock()
	defer registerMtx.Unlock()

	supportedWallets := make([]string, 0, len(wallets))
	for walletName := range wallets {
		supportedWallets = append(supportedWallets, walletName)
	}

	return supportedWallets
}
<commit_msg>lnwallet: remove NewRawKey and FetchRootKey from the WalletController interface<commit_after>package lnwallet

import (
	"errors"
	"fmt"
	"sync"

	"github.com/roasbeef/btcd/btcec"
	"github.com/roasbeef/btcd/chaincfg/chainhash"
	"github.com/roasbeef/btcd/wire"
	"github.com/roasbeef/btcutil"
)

// ErrNotMine is an error denoting that a WalletController instance is unable
// to spend a specified output.
var ErrNotMine = errors.New("the passed output doesn't belong to the wallet")

// AddressType is a enum-like type which denotes the possible address types
// WalletController supports.
type AddressType uint8

const (
	// UnknownAddressType represents an output with an unknown or non-standard
	// script.
	UnknownAddressType AddressType = iota

	// WitnessPubKey represents a p2wkh address.
	WitnessPubKey

	// NestedWitnessPubKey represents a p2sh output which is itself a
	// nested p2wkh output.
	NestedWitnessPubKey

	// PubKeyHash represents a regular p2pkh output.
	PubKeyHash
)

// ErrDoubleSpend is returned from PublishTransaction in case the
// tx being published is spending an output spent by a conflicting
// transaction.
var ErrDoubleSpend = errors.New("Transaction rejected: output already spent")

// Utxo is an unspent output denoted by its outpoint, and output value of the
// original output.
type Utxo struct {
	AddressType   AddressType
	Value         btcutil.Amount
	PkScript      []byte
	RedeemScript  []byte
	WitnessScript []byte
	wire.OutPoint
}

// TransactionDetail describes a transaction with either inputs which belong to
// the wallet, or has outputs that pay to the wallet.
type TransactionDetail struct {
	// Hash is the transaction hash of the transaction.
	Hash chainhash.Hash

	// Value is the net value of this transaction (in satoshis) from the
	// PoV of the wallet. If this transaction purely spends from the
	// wallet's funds, then this value will be negative. Similarly, if this
	// transaction credits the wallet, then this value will be positive.
	Value btcutil.Amount

	// NumConfirmations is the number of confirmations this transaction
	// has. If the transaction is unconfirmed, then this value will be
	// zero.
	NumConfirmations int32

	// BlockHeight is the hash of the block which includes this
	// transaction. Unconfirmed transactions will have a nil value for this
	// field.
	BlockHash *chainhash.Hash

	// BlockHeight is the height of the block including this transaction.
	// Unconfirmed transaction will show a height of zero.
	BlockHeight int32

	// Timestamp is the unix timestamp of the block including this
	// transaction. If the transaction is unconfirmed, then this will be a
	// timestamp of txn creation.
	Timestamp int64

	// TotalFees is the total fee in satoshis paid by this transaction.
	TotalFees int64

	// DestAddresses are the destinations for a transaction
	DestAddresses []btcutil.Address
}

// TransactionSubscription is an interface which describes an object capable of
// receiving notifications of new transaction related to the underlying wallet.
// TODO(roasbeef): add balance updates?
type TransactionSubscription interface {
	// ConfirmedTransactions returns a channel which will be sent on as new
	// relevant transactions are confirmed.
	ConfirmedTransactions() chan *TransactionDetail

	// UnconfirmedTransactions returns a channel which will be sent on as
	// new relevant transactions are seen within the network.
	UnconfirmedTransactions() chan *TransactionDetail

	// Cancel finalizes the subscription, cleaning up any resources
	// allocated.
	Cancel()
}

// WalletController defines an abstract interface for controlling a local Pure
// Go wallet, a local or remote wallet via an RPC mechanism, or possibly even
// a daemon assisted hardware wallet. This interface serves the purpose of
// allowing LightningWallet to be seamlessly compatible with several wallets
// such as: uspv, btcwallet, Bitcoin Core, Electrum, etc. This interface then
// serves as a "base wallet", with Lightning Network awareness taking place at
// a "higher" level of abstraction. Essentially, an overlay wallet.
// Implementors of this interface must closely adhere to the documented
// behavior of all interface methods in order to ensure identical behavior
// across all concrete implementations.
type WalletController interface {
	// FetchInputInfo queries for the WalletController's knowledge of the
	// passed outpoint. If the base wallet determines this output is under
	// its control, then the original txout should be returned. Otherwise,
	// a non-nil error value of ErrNotMine should be returned instead.
	FetchInputInfo(prevOut *wire.OutPoint) (*wire.TxOut, error)

	// ConfirmedBalance returns the sum of all the wallet's unspent outputs
	// that have at least confs confirmations. If confs is set to zero,
	// then all unspent outputs, including those currently in the mempool
	// will be included in the final sum.
	ConfirmedBalance(confs int32, witness bool) (btcutil.Amount, error)

	// NewAddress returns the next external or internal address for the
	// wallet dictated by the value of the `change` parameter. If change is
	// true, then an internal address should be used, otherwise an external
	// address should be returned. The type of address returned is dictated
	// by the wallet's capabilities, and may be of type: p2sh, p2pkh,
	// p2wkh, p2wsh, etc.
	NewAddress(addrType AddressType, change bool) (btcutil.Address, error)

	// GetPrivKey retrieves the underlying private key associated with the
	// passed address. If the wallet is unable to locate this private key
	// due to the address not being under control of the wallet, then an
	// error should be returned.
	GetPrivKey(a btcutil.Address) (*btcec.PrivateKey, error)

	// SendOutputs funds, signs, and broadcasts a Bitcoin transaction
	// paying out to the specified outputs. In the case the wallet has
	// insufficient funds, or the outputs are non-standard, an error should
	// be returned. This method also takes the target fee expressed in
	// sat/vbyte that should be used when crafting the transaction.
	SendOutputs(outputs []*wire.TxOut,
		feeRate SatPerVByte) (*chainhash.Hash, error)

	// ListUnspentWitness returns all unspent outputs which are version 0
	// witness programs. The 'confirms' parameter indicates the minimum
	// number of confirmations an output needs in order to be returned by
	// this method. Passing -1 as 'confirms' indicates that even
	// unconfirmed outputs should be returned.
	ListUnspentWitness(confirms int32) ([]*Utxo, error)

	// ListTransactionDetails returns a list of all transactions which are
	// relevant to the wallet.
	ListTransactionDetails() ([]*TransactionDetail, error)

	// LockOutpoint marks an outpoint as locked meaning it will no longer
	// be deemed as eligible for coin selection. Locking outputs are
	// utilized in order to avoid race conditions when selecting inputs for
	// usage when funding a channel.
	LockOutpoint(o wire.OutPoint)

	// UnlockOutpoint unlocks an previously locked output, marking it
	// eligible for coin selection.
	UnlockOutpoint(o wire.OutPoint)

	// PublishTransaction performs cursory validation (dust checks, etc),
	// then finally broadcasts the passed transaction to the Bitcoin network.
	// If the transaction is rejected because it is conflicting with an
	// already known transaction, ErrDoubleSpend is returned. If the
	// transaction is already known (published already), no error will be
	// returned. Other error returned depends on the currently active chain
	// backend.
	PublishTransaction(tx *wire.MsgTx) error

	// SubscribeTransactions returns a TransactionSubscription client which
	// is capable of receiving async notifications as new transactions
	// related to the wallet are seen within the network, or found in
	// blocks.
	//
	// NOTE: a non-nil error should be returned if notifications aren't
	// supported.
	//
	// TODO(roasbeef): make distinct interface?
	SubscribeTransactions() (TransactionSubscription, error)

	// IsSynced returns a boolean indicating if from the PoV of the wallet,
	// it has fully synced to the current best block in the main chain.
	// It also returns an int64 indicating the timestamp of the best block
	// known to the wallet, expressed in Unix epoch time
	IsSynced() (bool, int64, error)

	// Start initializes the wallet, making any necessary connections,
	// starting up required goroutines etc.
	Start() error

	// Stop signals the wallet for shutdown. Shutdown may entail closing
	// any active sockets, database handles, stopping goroutines, etc.
	Stop() error

	// BackEnd returns a name for the wallet's backing chain service,
	// which could be e.g. btcd, bitcoind, neutrino, or another consensus
	// service.
	BackEnd() string
}

// BlockChainIO is a dedicated source which will be used to obtain queries
// related to the current state of the blockchain. The data returned by each of
// the defined methods within this interface should always return the most up
// to date data possible.
//
// TODO(roasbeef): move to diff package perhaps?
// TODO(roasbeef): move publish txn here?
type BlockChainIO interface {
	// GetBestBlock returns the current height and block hash of the valid
	// most-work chain the implementation is aware of.
	GetBestBlock() (*chainhash.Hash, int32, error)

	// GetUtxo attempts to return the passed outpoint if it's still a
	// member of the utxo set. The passed height hint should be the "birth
	// height" of the passed outpoint. In the case that the output is in
	// the UTXO set, then the output corresponding to that output is
	// returned.  Otherwise, a non-nil error will be returned.
	GetUtxo(op *wire.OutPoint, heightHint uint32) (*wire.TxOut, error)

	// GetBlockHash returns the hash of the block in the best blockchain
	// at the given height.
	GetBlockHash(blockHeight int64) (*chainhash.Hash, error)

	// GetBlock returns the block in the main chain identified by the given
	// hash.
	GetBlock(blockHash *chainhash.Hash) (*wire.MsgBlock, error)
}

// Signer represents an abstract object capable of generating raw signatures as
// well as full complete input scripts given a valid SignDescriptor and
// transaction. This interface fully abstracts away signing paving the way for
// Signer implementations such as hardware wallets, hardware tokens, HSM's, or
// simply a regular wallet.
type Signer interface {
	// SignOutputRaw generates a signature for the passed transaction
	// according to the data within the passed SignDescriptor.
	//
	// NOTE: The resulting signature should be void of a sighash byte.
	SignOutputRaw(tx *wire.MsgTx, signDesc *SignDescriptor) ([]byte, error)

	// ComputeInputScript generates a complete InputIndex for the passed
	// transaction with the signature as defined within the passed
	// SignDescriptor. This method should be capable of generating the
	// proper input script for both regular p2wkh output and p2wkh outputs
	// nested within a regular p2sh output.
	//
	// NOTE: This method will ignore any tweak parameters set within the
	// passed SignDescriptor as it assumes a set of typical script
	// templates (p2wkh, np2wkh, etc).
	ComputeInputScript(tx *wire.MsgTx, signDesc *SignDescriptor) (*InputScript, error)
}

// MessageSigner represents an abstract object capable of signing arbitrary
// messages. The capabilities of this interface are used to sign announcements
// to the network, or just arbitrary messages that leverage the wallet's keys
// to attest to some message.
type MessageSigner interface {
	// SignMessage attempts to sign a target message with the private key
	// that corresponds to the passed public key. If the target private key
	// is unable to be found, then an error will be returned. The actual
	// digest signed is the double SHA-256 of the passed message.
	SignMessage(pubKey *btcec.PublicKey, msg []byte) (*btcec.Signature, error)
}

// PreimageCache is an interface that represents a global cache for preimages.
// We'll utilize this cache to communicate the discovery of new preimages
// across sub-systems.
type PreimageCache interface {
	// LookupPreimage attempts to look up a preimage according to its hash.
	// If found, the preimage is returned along with true for the second
	// argument. Otherwise, it'll return false.
	LookupPreimage(hash []byte) ([]byte, bool)

	// AddPreimage attempts to add a new preimage to the global cache. If
	// successful a nil error will be returned.
	AddPreimage(preimage []byte) error
}

// WalletDriver represents a "driver" for a particular concrete
// WalletController implementation. A driver is identified by a globally unique
// string identifier along with a 'New()' method which is responsible for
// initializing a particular WalletController concrete implementation.
type WalletDriver struct {
	// WalletType is a string which uniquely identifies the
	// WalletController that this driver, drives.
	WalletType string

	// New creates a new instance of a concrete WalletController
	// implementation given a variadic set up arguments. The function takes
	// a variadic number of interface parameters in order to provide
	// initialization flexibility, thereby accommodating several potential
	// WalletController implementations.
	New func(args ...interface{}) (WalletController, error)

	// BackEnds returns a list of available chain service drivers for the
	// wallet driver. This could be e.g. bitcoind, btcd, neutrino, etc.
	BackEnds func() []string
}

var (
	wallets     = make(map[string]*WalletDriver)
	registerMtx sync.Mutex
)

// RegisteredWallets returns a slice of all currently registered notifiers.
//
// NOTE: This function is safe for concurrent access.
func RegisteredWallets() []*WalletDriver {
	registerMtx.Lock()
	defer registerMtx.Unlock()

	registeredWallets := make([]*WalletDriver, 0, len(wallets))
	for _, wallet := range wallets {
		registeredWallets = append(registeredWallets, wallet)
	}

	return registeredWallets
}

// RegisterWallet registers a WalletDriver which is capable of driving a
// concrete WalletController interface. In the case that this driver has
// already been registered, an error is returned.
//
// NOTE: This function is safe for concurrent access.
func RegisterWallet(driver *WalletDriver) error {
	registerMtx.Lock()
	defer registerMtx.Unlock()

	if _, ok := wallets[driver.WalletType]; ok {
		return fmt.Errorf("wallet already registered")
	}

	wallets[driver.WalletType] = driver

	return nil
}

// SupportedWallets returns a slice of strings that represents the wallet
// drivers that have been registered and are therefore supported.
//
// NOTE: This function is safe for concurrent access.
func SupportedWallets() []string {
	registerMtx.Lock()
	defer registerMtx.Unlock()

	supportedWallets := make([]string, 0, len(wallets))
	for walletName := range wallets {
		supportedWallets = append(supportedWallets, walletName)
	}

	return supportedWallets
}
<|endoftext|>