<commit_before>package tools_test

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"runtime"
	"sort"
	"strings"
	"sync"
	"testing"

	"github.com/github/git-lfs/tools"
	"github.com/stretchr/testify/assert"
)

func TestCleanPathsCleansPaths(t *testing.T) {
	cleaned := tools.CleanPaths("/foo/bar/,/foo/bar/baz", ",")

	assert.Equal(t, []string{"/foo/bar", "/foo/bar/baz"}, cleaned)
}

func TestCleanPathsReturnsNoResultsWhenGivenNoPaths(t *testing.T) {
	cleaned := tools.CleanPaths("", ",")

	assert.Empty(t, cleaned)
}

type TestIncludeExcludeCase struct {
	expectedResult bool
	includes       []string
	excludes       []string
}

func TestFilterIncludeExclude(t *testing.T) {

	cases := []TestIncludeExcludeCase{
		// Null case
		TestIncludeExcludeCase{true, nil, nil},
		// Inclusion
		TestIncludeExcludeCase{true, []string{"*.dat"}, nil},
		TestIncludeExcludeCase{true, []string{"file*.dat"}, nil},
		TestIncludeExcludeCase{true, []string{"file*"}, nil},
		TestIncludeExcludeCase{true, []string{"*name.dat"}, nil},
		TestIncludeExcludeCase{true, []string{"test/filename.dat"}, nil},
		TestIncludeExcludeCase{true, []string{"test/filename.dat"}, nil},
		TestIncludeExcludeCase{false, []string{"blank", "something", "foo"}, nil},
		TestIncludeExcludeCase{false, []string{"test/notfilename.dat"}, nil},
		TestIncludeExcludeCase{true, []string{"test"}, nil},
		TestIncludeExcludeCase{true, []string{"test/*"}, nil},
		TestIncludeExcludeCase{false, []string{"nottest"}, nil},
		TestIncludeExcludeCase{false, []string{"nottest/*"}, nil},
		TestIncludeExcludeCase{true, []string{"test/fil*"}, nil},
		TestIncludeExcludeCase{false, []string{"test/g*"}, nil},
		TestIncludeExcludeCase{true, []string{"tes*/*"}, nil},
		// Exclusion
		TestIncludeExcludeCase{false, nil, []string{"*.dat"}},
		TestIncludeExcludeCase{false, nil, []string{"file*.dat"}},
		TestIncludeExcludeCase{false, nil, []string{"file*"}},
		TestIncludeExcludeCase{false, nil, []string{"*name.dat"}},
		TestIncludeExcludeCase{false, nil, []string{"test/filename.dat"}},
		TestIncludeExcludeCase{false, nil, []string{"blank", "something", "test/filename.dat", "foo"}},
		TestIncludeExcludeCase{true, nil, []string{"blank", "something", "foo"}},
		TestIncludeExcludeCase{true, nil, []string{"test/notfilename.dat"}},
		TestIncludeExcludeCase{false, nil, []string{"test"}},
		TestIncludeExcludeCase{false, nil, []string{"test/*"}},
		TestIncludeExcludeCase{true, nil, []string{"nottest"}},
		TestIncludeExcludeCase{true, nil, []string{"nottest/*"}},
		TestIncludeExcludeCase{false, nil, []string{"test/fil*"}},
		TestIncludeExcludeCase{true, nil, []string{"test/g*"}},
		TestIncludeExcludeCase{false, nil, []string{"tes*/*"}},

		// Both
		TestIncludeExcludeCase{true, []string{"test/filename.dat"}, []string{"test/notfilename.dat"}},
		TestIncludeExcludeCase{false, []string{"test"}, []string{"test/filename.dat"}},
		TestIncludeExcludeCase{true, []string{"test/*"}, []string{"test/notfile*"}},
		TestIncludeExcludeCase{false, []string{"test/*"}, []string{"test/file*"}},
		TestIncludeExcludeCase{false, []string{"another/*", "test/*"}, []string{"test/notfilename.dat", "test/filename.dat"}},
	}

	for _, c := range cases {
		assert.Equal(t, c.expectedResult, tools.FilenamePassesIncludeExcludeFilter("test/filename.dat", c.includes, c.excludes), c)
		if runtime.GOOS == "windows" {
			// also test with \ path separators, tolerate mixed separators
			for i, inc := range c.includes {
				c.includes[i] = strings.Replace(inc, "/", "\\", -1)
			}
			for i, ex := range c.excludes {
				c.excludes[i] = strings.Replace(ex, "/", "\\", -1)
			}
			assert.Equal(t, c.expectedResult, tools.FilenamePassesIncludeExcludeFilter("test/filename.dat", c.includes, c.excludes), c)
		}
	}
}

func TestFastWalkBasic(t *testing.T) {
	rootDir, err := ioutil.TempDir(os.TempDir(), "GitLfsTestFastWalkBasic")
	if err != nil {
		assert.FailNow(t, "Unable to get temp dir: %v", err)
	}

	defer os.RemoveAll(rootDir)
	os.Chdir(rootDir)
	dirs := []string{
		"testroot",
		"testroot/folder1",
		"testroot/folder2",
		"testroot/folder2/subfolder1",
		"testroot/folder2/subfolder2",
		"testroot/folder2/subfolder3",
		"testroot/folder2/subfolder4",
		"testroot/folder2/subfolder4/subsub",
	}
	expectedEntries := make([]string, 0, 250)

	for i, dir := range dirs {
		os.MkdirAll(dir, 0755)
		numFiles := 10
		expectedEntries = append(expectedEntries, dir)
		if i >= 3 && i <= 5 {
			// Bulk test to ensure works with > 1 batch
			numFiles = 160
		}
		for f := 0; f < numFiles; f++ {
			filename := filepath.Join(dir, fmt.Sprintf("file%d.txt", f))
			ioutil.WriteFile(filename, []byte("TEST"), 0644)
			expectedEntries = append(expectedEntries, filename)
		}
	}

	fchan, errchan := tools.FastWalk(dirs[0], nil, nil)
	gotEntries, gotErrors := collectFastWalkResults(fchan, errchan)

	assert.Equal(t, 0, len(gotErrors))

	sort.Strings(expectedEntries)
	sort.Strings(gotEntries)
	assert.Equal(t, expectedEntries, gotEntries)

}

func collectFastWalkResults(fchan <-chan tools.FastWalkInfo, errchan <-chan error) ([]string, []error) {
	gotEntries := make([]string, 0, 1000)
	gotErrors := make([]error, 0, 5)
	var waitg sync.WaitGroup
	waitg.Add(2)
	go func() {
		for o := range fchan {
			gotEntries = append(gotEntries, filepath.Join(o.ParentDir, o.Info.Name()))
		}
		waitg.Done()
	}()
	go func() {
		for err := range errchan {
			gotErrors = append(gotErrors, err)
		}
		waitg.Done()
	}()
	waitg.Wait()

	return gotEntries, gotErrors
}
<commit_msg>Couple more negative tests<commit_after>package tools_test

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"runtime"
	"sort"
	"strings"
	"sync"
	"testing"

	"github.com/github/git-lfs/tools"
	"github.com/stretchr/testify/assert"
)

func TestCleanPathsCleansPaths(t *testing.T) {
	cleaned := tools.CleanPaths("/foo/bar/,/foo/bar/baz", ",")

	assert.Equal(t, []string{"/foo/bar", "/foo/bar/baz"}, cleaned)
}

func TestCleanPathsReturnsNoResultsWhenGivenNoPaths(t *testing.T) {
	cleaned := tools.CleanPaths("", ",")

	assert.Empty(t, cleaned)
}

type TestIncludeExcludeCase struct {
	expectedResult bool
	includes       []string
	excludes       []string
}

func TestFilterIncludeExclude(t *testing.T) {

	cases := []TestIncludeExcludeCase{
		// Null case
		TestIncludeExcludeCase{true, nil, nil},
		// Inclusion
		TestIncludeExcludeCase{true, []string{"*.dat"}, nil},
		TestIncludeExcludeCase{true, []string{"file*.dat"}, nil},
		TestIncludeExcludeCase{true, []string{"file*"}, nil},
		TestIncludeExcludeCase{true, []string{"*name.dat"}, nil},
		TestIncludeExcludeCase{false, []string{"/*.dat"}, nil},
		TestIncludeExcludeCase{false, []string{"otherfolder/*.dat"}, nil},
		TestIncludeExcludeCase{true, []string{"test/filename.dat"}, nil},
		TestIncludeExcludeCase{true, []string{"test/filename.dat"}, nil},
		TestIncludeExcludeCase{false, []string{"blank", "something", "foo"}, nil},
		TestIncludeExcludeCase{false, []string{"test/notfilename.dat"}, nil},
		TestIncludeExcludeCase{true, []string{"test"}, nil},
		TestIncludeExcludeCase{true, []string{"test/*"}, nil},
		TestIncludeExcludeCase{false, []string{"nottest"}, nil},
		TestIncludeExcludeCase{false, []string{"nottest/*"}, nil},
		TestIncludeExcludeCase{true, []string{"test/fil*"}, nil},
		TestIncludeExcludeCase{false, []string{"test/g*"}, nil},
		TestIncludeExcludeCase{true, []string{"tes*/*"}, nil},
		// Exclusion
		TestIncludeExcludeCase{false, nil, []string{"*.dat"}},
		TestIncludeExcludeCase{false, nil, []string{"file*.dat"}},
		TestIncludeExcludeCase{false, nil, []string{"file*"}},
		TestIncludeExcludeCase{false, nil, []string{"*name.dat"}},
		TestIncludeExcludeCase{true, nil, []string{"/*.dat"}},
		TestIncludeExcludeCase{true, nil, []string{"otherfolder/*.dat"}},
		TestIncludeExcludeCase{false, nil, []string{"test/filename.dat"}},
		TestIncludeExcludeCase{false, nil, []string{"blank", "something", "test/filename.dat", "foo"}},
		TestIncludeExcludeCase{true, nil, []string{"blank", "something", "foo"}},
		TestIncludeExcludeCase{true, nil, []string{"test/notfilename.dat"}},
		TestIncludeExcludeCase{false, nil, []string{"test"}},
		TestIncludeExcludeCase{false, nil, []string{"test/*"}},
		TestIncludeExcludeCase{true, nil, []string{"nottest"}},
		TestIncludeExcludeCase{true, nil, []string{"nottest/*"}},
		TestIncludeExcludeCase{false, nil, []string{"test/fil*"}},
		TestIncludeExcludeCase{true, nil, []string{"test/g*"}},
		TestIncludeExcludeCase{false, nil, []string{"tes*/*"}},

		// Both
		TestIncludeExcludeCase{true, []string{"test/filename.dat"}, []string{"test/notfilename.dat"}},
		TestIncludeExcludeCase{false, []string{"test"}, []string{"test/filename.dat"}},
		TestIncludeExcludeCase{true, []string{"test/*"}, []string{"test/notfile*"}},
		TestIncludeExcludeCase{false, []string{"test/*"}, []string{"test/file*"}},
		TestIncludeExcludeCase{false, []string{"another/*", "test/*"}, []string{"test/notfilename.dat", "test/filename.dat"}},
	}

	for _, c := range cases {
		assert.Equal(t, c.expectedResult, tools.FilenamePassesIncludeExcludeFilter("test/filename.dat", c.includes, c.excludes), c)
		if runtime.GOOS == "windows" {
			// also test with \ path separators, tolerate mixed separators
			for i, inc := range c.includes {
				c.includes[i] = strings.Replace(inc, "/", "\\", -1)
			}
			for i, ex := range c.excludes {
				c.excludes[i] = strings.Replace(ex, "/", "\\", -1)
			}
			assert.Equal(t, c.expectedResult, tools.FilenamePassesIncludeExcludeFilter("test/filename.dat", c.includes, c.excludes), c)
		}
	}
}

func TestFastWalkBasic(t *testing.T) {
	rootDir, err := ioutil.TempDir(os.TempDir(), "GitLfsTestFastWalkBasic")
	if err != nil {
		assert.FailNow(t, "Unable to get temp dir: %v", err)
	}

	defer os.RemoveAll(rootDir)
	os.Chdir(rootDir)
	dirs := []string{
		"testroot",
		"testroot/folder1",
		"testroot/folder2",
		"testroot/folder2/subfolder1",
		"testroot/folder2/subfolder2",
		"testroot/folder2/subfolder3",
		"testroot/folder2/subfolder4",
		"testroot/folder2/subfolder4/subsub",
	}
	expectedEntries := make([]string, 0, 250)

	for i, dir := range dirs {
		os.MkdirAll(dir, 0755)
		numFiles := 10
		expectedEntries = append(expectedEntries, dir)
		if i >= 3 && i <= 5 {
			// Bulk test to ensure works with > 1 batch
			numFiles = 160
		}
		for f := 0; f < numFiles; f++ {
			filename := filepath.Join(dir, fmt.Sprintf("file%d.txt", f))
			ioutil.WriteFile(filename, []byte("TEST"), 0644)
			expectedEntries = append(expectedEntries, filename)
		}
	}

	fchan, errchan := tools.FastWalk(dirs[0], nil, nil)
	gotEntries, gotErrors := collectFastWalkResults(fchan, errchan)

	assert.Equal(t, 0, len(gotErrors))

	sort.Strings(expectedEntries)
	sort.Strings(gotEntries)
	assert.Equal(t, expectedEntries, gotEntries)

}

func collectFastWalkResults(fchan <-chan tools.FastWalkInfo, errchan <-chan error) ([]string, []error) {
	gotEntries := make([]string, 0, 1000)
	gotErrors := make([]error, 0, 5)
	var waitg sync.WaitGroup
	waitg.Add(2)
	go func() {
		for o := range fchan {
			gotEntries = append(gotEntries, filepath.Join(o.ParentDir, o.Info.Name()))
		}
		waitg.Done()
	}()
	go func() {
		for err := range errchan {
			gotErrors = append(gotErrors, err)
		}
		waitg.Done()
	}()
	waitg.Wait()

	return gotEntries, gotErrors
}
<|endoftext|>


<commit_before>package rocserv

import (
	"context"
	"sync"
	"time"

	"github.com/shawnfeng/sutil/slog"
)

const (
	defaultCapacity    = 32 // 初始连接wrapper数，可以比较小
	defaultMaxCapacity = 512
	defaultIdleTimeout = time.Second * 120
)

// ClientPool every addr has a connection pool, each backend server has more than one addr, in client side, it's ClientPool
type ClientPool struct {
	calleeServiceKey string

	capacity    int
	maxCapacity int
	idleTimeout time.Duration
	clientPool  sync.Map
	rpcFactory  func(addr string) (rpcClientConn, error)
}

// NewClientPool constructor of pool, 如果连接数过低，修正为默认值
func NewClientPool(capacity, maxCapacity int, rpcFactory func(addr string) (rpcClientConn, error), calleeServiceKey string) *ClientPool {
	return &ClientPool{capacity: capacity, maxCapacity: maxCapacity, rpcFactory: rpcFactory, calleeServiceKey: calleeServiceKey, idleTimeout: defaultIdleTimeout}
}

// Get get connection from pool, if reach max, create new connection and return
func (m *ClientPool) Get(addr string) (rpcClientConn, error) {
	fun := "ClientPool.Get -->"
	cp := m.getPool(addr)
	ctx, cancel := context.WithTimeout(context.Background(), getConnTimeout)
	defer cancel()
	c, err := cp.Get(ctx)
	if err != nil {
		slog.Errorf("%s get conn from connection pool failed, callee_service: %s, addr: %s, err: %v", fun, m.calleeServiceKey, addr, err)
		return nil, err
	}
	return c.(rpcClientConn), nil
}

// Put 连接池回收连接
func (m *ClientPool) Put(addr string, client rpcClientConn, err error) {
	fun := "ClientPool.Put -->"
	cp := m.getPool(addr)
	// close client and don't put to pool
	if err != nil {
		slog.Warnf("%s put rpc client to pool with err: %v, callee_service: %s, addr: %s", fun, err, m.calleeServiceKey, addr)
		client.Close()
		cp.Put(nil)
		return
	}
	cp.Put(client)
}

// Close close connection pool in client pool
func (m *ClientPool) Close() {
	closeConnectionPool := func(key, value interface{}) bool {
		if connectionPool, ok := value.(*ConnectionPool); ok {
			connectionPool.Close()
		}
		return true
	}
	m.clientPool.Range(closeConnectionPool)
}

func (m *ClientPool) getPool(addr string) *ConnectionPool {
	fun := "ClientPool.getPool -->"
	var cp *ConnectionPool
	value, ok := m.clientPool.Load(addr)
	if ok == true {
		cp = value.(*ConnectionPool)
	} else {
		slog.Infof("%s not found connection pool of callee_service: %s, addr: %s, create it", fun, m.calleeServiceKey, addr)
		cp = NewConnectionPool(addr, m.capacity, m.maxCapacity, m.idleTimeout, m.rpcFactory, m.calleeServiceKey)
		cp.Open()
		m.clientPool.Store(addr, cp)
	}
	return cp
}
<commit_msg>fix client pool bug<commit_after>package rocserv

import (
	"context"
	"sync"
	"time"

	"github.com/shawnfeng/sutil/slog"
)

const (
	defaultCapacity    = 32 // 初始连接wrapper数，可以比较小
	defaultMaxCapacity = 512
	defaultIdleTimeout = time.Second * 120
)

// ClientPool every addr has a connection pool, each backend server has more than one addr, in client side, it's ClientPool
type ClientPool struct {
	calleeServiceKey string
	mu               sync.Mutex
	capacity         int
	maxCapacity      int
	idleTimeout      time.Duration
	clientPool       sync.Map
	rpcFactory       func(addr string) (rpcClientConn, error)
}

// NewClientPool constructor of pool, 如果连接数过低，修正为默认值
func NewClientPool(capacity, maxCapacity int, rpcFactory func(addr string) (rpcClientConn, error), calleeServiceKey string) *ClientPool {
	return &ClientPool{capacity: capacity, maxCapacity: maxCapacity, rpcFactory: rpcFactory, calleeServiceKey: calleeServiceKey, idleTimeout: defaultIdleTimeout}
}

// Get get connection from pool, if reach max, create new connection and return
func (m *ClientPool) Get(addr string) (rpcClientConn, error) {
	fun := "ClientPool.Get -->"
	cp := m.getPool(addr)
	ctx, cancel := context.WithTimeout(context.Background(), getConnTimeout)
	defer cancel()
	c, err := cp.Get(ctx)
	if err != nil {
		slog.Errorf("%s get conn from connection pool failed, callee_service: %s, addr: %s, err: %v", fun, m.calleeServiceKey, addr, err)
		return nil, err
	}
	return c.(rpcClientConn), nil
}

// Put 连接池回收连接
func (m *ClientPool) Put(addr string, client rpcClientConn, err error) {
	fun := "ClientPool.Put -->"
	cp := m.getPool(addr)
	// close client and don't put to pool
	if err != nil {
		slog.Warnf("%s put rpc client to pool with err: %v, callee_service: %s, addr: %s", fun, err, m.calleeServiceKey, addr)
		client.Close()
		cp.Put(nil)
		return
	}
	cp.Put(client)
}

// Close close connection pool in client pool
func (m *ClientPool) Close() {
	closeConnectionPool := func(key, value interface{}) bool {
		if connectionPool, ok := value.(*ConnectionPool); ok {
			connectionPool.Close()
		}
		return true
	}
	m.clientPool.Range(closeConnectionPool)
}

func (m *ClientPool) getPool(addr string) *ConnectionPool {
	fun := "ClientPool.getPool -->"
	var cp *ConnectionPool
	value, ok := m.clientPool.Load(addr)
	if ok == true {
		cp = value.(*ConnectionPool)
	} else {
		m.mu.Lock()
		defer m.mu.Unlock()
		value, ok := m.clientPool.Load(addr)
		if ok == true {
			cp = value.(*ConnectionPool)
		} else {
			slog.Infof("%s not found connection pool of callee_service: %s, addr: %s, create it", fun, m.calleeServiceKey, addr)
			cp = NewConnectionPool(addr, m.capacity, m.maxCapacity, m.idleTimeout, m.rpcFactory, m.calleeServiceKey)
			cp.Open()
			m.clientPool.Store(addr, cp)
		}
	}
	return cp
}
<|endoftext|>


<commit_before>package validator

import (
	"reflect"
	"strconv"

	"github.com/asaskevich/govalidator"
	"github.com/NyaaPantsu/nyaa/util/log"
	msg "github.com/NyaaPantsu/nyaa/util/messages"
)
/

// ValidateForm : Check if a form is valid according to its tags
func ValidateForm(form interface{}, mes *msg.Messages) {
	result, err := govalidator.ValidateStruct(post)
	if err != nil {
		println("error: " + err.Error())
	}
}
<commit_msg>Fix typo'd variable<commit_after>package validator

import (
	"reflect"
	"strconv"

	"github.com/asaskevich/govalidator"
	"github.com/NyaaPantsu/nyaa/util/log"
	msg "github.com/NyaaPantsu/nyaa/util/messages"
)
/

// ValidateForm : Check if a form is valid according to its tags
func ValidateForm(form interface{}, mes *msg.Messages) {
	result, err := govalidator.ValidateStruct(form)
	if err != nil {
		println("error: " + err.Error())
	}
}
<|endoftext|>


<commit_before>package brimstore

import (
	"os"
	"strconv"
	"sync"
	"sync/atomic"
	"unsafe"

	"github.com/gholt/brimutil"
)

const (
	_VALUESBLOCK_UNUSED   = iota
	_VALUESBLOCK_TOMB     = iota
	_VALUESBLOCK_IDOFFSET = iota
)

type valuesLocMap struct {
	leftMask   uint64
	a          *valuesLocStore
	b          *valuesLocStore
	c          *valuesLocMap
	d          *valuesLocMap
	resizing   bool
	resizeLock sync.Mutex
	cores      int
	splitCount int
}

type valuesLocStore struct {
	buckets []valueLoc
	locks   []sync.RWMutex
	used    int32
}

type valueLoc struct {
	next    *valueLoc
	keyA    uint64
	keyB    uint64
	seq     uint64
	blockID uint16
	offset  uint32
	length  uint32
}

type valuesLocMapStats struct {
	depth        uint64
	depthCounts  []uint64
	sections     uint64
	storages     uint64
	buckets      uint64
	bucketCounts []uint64
	splitCount   uint64
	locs         uint64
	pointerLocs  uint64
	unused       uint64
	tombs        uint64
	used         uint64
	length       uint64
}

func newValuesLocMap(opts *ValuesStoreOpts) *valuesLocMap {
	if opts == nil {
		opts = NewValuesStoreOpts()
	}
	cores := opts.Cores
	if cores < 1 {
		cores = 1
	}
	valuesLocMapPageSize := opts.ValuesLocMapPageSize
	if env := os.Getenv("BRIMSTORE_VALUESSTORE_VALUESLOCMAP_PAGESIZE"); env != "" {
		if val, err := strconv.Atoi(env); err == nil {
			valuesLocMapPageSize = val
		}
	}
	if valuesLocMapPageSize < 4096 {
		valuesLocMapPageSize = 4096
	}
	bucketCount := 1 << brimutil.PowerOfTwoNeeded(uint64(valuesLocMapPageSize)/uint64(unsafe.Sizeof(valueLoc{})))
	lockCount := 1 << brimutil.PowerOfTwoNeeded(uint64(cores*cores))
	if lockCount > bucketCount {
		lockCount = bucketCount
	}
	return &valuesLocMap{
		leftMask: uint64(1) << 63,
		a: &valuesLocStore{
			buckets: make([]valueLoc, bucketCount),
			locks:   make([]sync.RWMutex, lockCount),
		},
		cores:      cores,
		splitCount: bucketCount * 2,
	}
}

func (vlm *valuesLocMap) get(keyA uint64, keyB uint64) (uint64, uint16, uint32, uint32) {
	var seq uint64
	var blockID uint16 = _VALUESBLOCK_UNUSED
	var offset uint32
	var length uint32
	var a *valuesLocStore
	var b *valuesLocStore
	for {
		a = vlm.a
		b = vlm.b
		c := vlm.c
		d := vlm.d
		if c == nil {
			break
		}
		if keyA&vlm.leftMask == 0 {
			vlm = c
		} else {
			vlm = d
		}
	}
	if b != nil {
		if keyA&vlm.leftMask == 0 {
			b = nil
		} else {
			a, b = b, a
		}
	}
	bix := keyB % uint64(len(a.buckets))
	lix := bix % uint64(len(a.locks))
	a.locks[lix].RLock()
	if b != nil {
		b.locks[lix].RLock()
	}
	for itemA := &a.buckets[bix]; itemA != nil; itemA = itemA.next {
		if itemA.blockID != _VALUESBLOCK_UNUSED && itemA.keyA == keyA && itemA.keyB == keyB {
			seq, blockID, offset, length = itemA.seq, itemA.blockID, itemA.offset, itemA.length
			break
		}
	}
	if blockID == _VALUESBLOCK_UNUSED && b != nil {
		for itemB := &b.buckets[bix]; itemB != nil; itemB = itemB.next {
			if itemB.blockID != _VALUESBLOCK_UNUSED && itemB.keyA == keyA && itemB.keyB == keyB {
				seq, blockID, offset, length = itemB.seq, itemB.blockID, itemB.offset, itemB.length
				break
			}
		}
	}
	if b != nil {
		b.locks[lix].RUnlock()
	}
	a.locks[lix].RUnlock()
	return seq, blockID, offset, length
}

func (vlm *valuesLocMap) set(keyA uint64, keyB uint64, seq uint64, blockID uint16, offset uint32, length uint32, evenIfSameSeq bool) uint64 {
	var oldSeq uint64
	var a *valuesLocStore
	var b *valuesLocStore
	for {
		a = vlm.a
		b = vlm.b
		c := vlm.c
		d := vlm.d
		if c == nil {
			break
		}
		if keyA&vlm.leftMask == 0 {
			vlm = c
		} else {
			vlm = d
		}
	}
	if b != nil {
		if keyA&vlm.leftMask == 0 {
			b = nil
		} else {
			a, b = b, a
		}
	}
	bix := keyB % uint64(len(a.buckets))
	lix := bix % uint64(len(a.locks))
	done := false
	var unusedItemA *valueLoc
	a.locks[lix].Lock()
	if b != nil {
		b.locks[lix].Lock()
	}
	for itemA := &a.buckets[bix]; itemA != nil; itemA = itemA.next {
		if itemA.blockID == _VALUESBLOCK_UNUSED {
			if unusedItemA == nil {
				unusedItemA = itemA
			}
			continue
		}
		if itemA.keyA == keyA && itemA.keyB == keyB {
			oldSeq = itemA.seq
			if (evenIfSameSeq && itemA.seq == seq) || itemA.seq < seq {
				if blockID == _VALUESBLOCK_UNUSED {
					atomic.AddInt32(&a.used, -1)
				}
				itemA.seq = seq
				itemA.blockID = blockID
				itemA.offset = offset
				itemA.length = length
			}
			done = true
			break
		}
	}
	if !done && b != nil {
		for itemB := &b.buckets[bix]; itemB != nil; itemB = itemB.next {
			if itemB.blockID == _VALUESBLOCK_UNUSED {
				continue
			}
			if itemB.keyA == keyA && itemB.keyB == keyB {
				oldSeq = itemB.seq
				if (evenIfSameSeq && itemB.seq == seq) || itemB.seq < seq {
					atomic.AddInt32(&b.used, -1)
					itemB.blockID = _VALUESBLOCK_UNUSED
				} else {
					done = true
				}
				break
			}
		}
	}
	if !done && blockID != _VALUESBLOCK_UNUSED {
		atomic.AddInt32(&a.used, 1)
		if unusedItemA != nil {
			unusedItemA.keyA = keyA
			unusedItemA.keyB = keyB
			unusedItemA.seq = seq
			unusedItemA.blockID = blockID
			unusedItemA.offset = offset
			unusedItemA.length = length
		} else {
			a.buckets[bix].next = &valueLoc{
				next:    a.buckets[bix].next,
				keyA:    keyA,
				keyB:    keyB,
				seq:     seq,
				blockID: blockID,
				offset:  offset,
				length:  length,
			}
		}
	}
	if b != nil {
		b.locks[lix].Unlock()
	}
	a.locks[lix].Unlock()
	if b == nil {
		if int(atomic.LoadInt32(&a.used)) > vlm.splitCount {
			go vlm.split()
		}
	}
	return oldSeq
}

func (vlm *valuesLocMap) isResizing() bool {
	c, d := vlm.c, vlm.d
	return vlm.resizing || (c != nil && c.isResizing()) || (d != nil && d.isResizing())
}

func (vlm *valuesLocMap) gatherStats() *valuesLocMapStats {
	buckets := 0
	for llm := vlm; llm != nil; llm = llm.c {
		a := llm.a
		if a != nil {
			buckets = len(a.buckets)
			break
		}
	}
	stats := &valuesLocMapStats{
		depthCounts:  []uint64{0},
		buckets:      uint64(buckets),
		bucketCounts: make([]uint64, buckets),
		splitCount:   uint64(vlm.splitCount),
	}
	vlm.gatherStatsHelper(stats)
	stats.depthCounts = stats.depthCounts[1:]
	return stats
}

func (vlm *valuesLocMap) gatherStatsHelper(stats *valuesLocMapStats) {
	stats.sections++
	stats.depth++
	if stats.depth < uint64(len(stats.depthCounts)) {
		stats.depthCounts[stats.depth]++
	} else {
		stats.depthCounts = append(stats.depthCounts, 1)
	}
	a := vlm.a
	b := vlm.b
	c := vlm.c
	d := vlm.d
	for _, s := range []*valuesLocStore{a, b} {
		if s != nil {
			stats.storages++
			for bix := len(s.buckets) - 1; bix >= 0; bix-- {
				lix := bix % len(s.locks)
				s.locks[lix].RLock()
				for item := &s.buckets[bix]; item != nil; item = item.next {
					stats.bucketCounts[bix]++
					if item.next != nil {
						stats.pointerLocs++
					}
					stats.locs++
					switch item.blockID {
					case _VALUESBLOCK_UNUSED:
						stats.unused++
					case _VALUESBLOCK_TOMB:
						stats.tombs++
					default:
						stats.used++
						stats.length += uint64(item.length)
					}
				}
				s.locks[lix].RUnlock()
			}
		}
	}
	depthOrig := stats.depth
	if c != nil {
		c.gatherStatsHelper(stats)
		depthC := stats.depth
		stats.depth = depthOrig
		d.gatherStatsHelper(stats)
		if depthC > stats.depth {
			stats.depth = depthC
		}
	}
}

func (vlm *valuesLocMap) split() {
	if vlm.resizing {
		return
	}
	vlm.resizeLock.Lock()
	a := vlm.a
	b := vlm.b
	if vlm.resizing || a == nil || b != nil || int(atomic.LoadInt32(&a.used)) < vlm.splitCount {
		vlm.resizeLock.Unlock()
		return
	}
	vlm.resizing = true
	vlm.resizeLock.Unlock()
	b = &valuesLocStore{
		buckets: make([]valueLoc, len(a.buckets)),
		locks:   make([]sync.RWMutex, len(a.locks)),
	}
	vlm.b = b
	wg := &sync.WaitGroup{}
	wg.Add(vlm.cores)
	for core := 0; core < vlm.cores; core++ {
		go func(coreOffset int) {
			clean := false
			for !clean {
				clean = true
				for bix := len(a.buckets) - 1 - coreOffset; bix >= 0; bix -= vlm.cores {
					lix := bix % len(a.locks)
					b.locks[lix].Lock()
					a.locks[lix].Lock()
				NEXT_ITEM_A:
					for itemA := &a.buckets[bix]; itemA != nil; itemA = itemA.next {
						if itemA.blockID == _VALUESBLOCK_UNUSED || itemA.keyA&vlm.leftMask == 0 {
							continue
						}
						clean = false
						var unusedItemB *valueLoc
						for itemB := &b.buckets[bix]; itemB != nil; itemB = itemB.next {
							if itemB.blockID == _VALUESBLOCK_UNUSED {
								if unusedItemB == nil {
									unusedItemB = itemB
								}
								continue
							}
							if itemA.keyA == itemB.keyA && itemA.keyB == itemB.keyB {
								if itemA.seq > itemB.seq {
									itemB.keyA = itemA.keyA
									itemB.keyB = itemA.keyB
									itemB.seq = itemA.seq
									itemB.blockID = itemA.blockID
									itemB.offset = itemA.offset
									itemB.length = itemA.length
								}
								atomic.AddInt32(&a.used, -1)
								itemA.blockID = _VALUESBLOCK_UNUSED
								continue NEXT_ITEM_A
							}
						}
						atomic.AddInt32(&b.used, 1)
						if unusedItemB != nil {
							unusedItemB.keyA = itemA.keyA
							unusedItemB.keyB = itemA.keyB
							unusedItemB.seq = itemA.seq
							unusedItemB.blockID = itemA.blockID
							unusedItemB.offset = itemA.offset
							unusedItemB.length = itemA.length
						} else {
							b.buckets[bix].next = &valueLoc{
								next:    b.buckets[bix].next,
								keyA:    itemA.keyA,
								keyB:    itemA.keyB,
								seq:     itemA.seq,
								blockID: itemA.blockID,
								offset:  itemA.offset,
								length:  itemA.length,
							}
						}
						atomic.AddInt32(&a.used, -1)
						itemA.blockID = _VALUESBLOCK_UNUSED
					}
					a.locks[lix].Unlock()
					b.locks[lix].Unlock()
				}
			}
			wg.Done()
		}(core)
	}
	wg.Wait()
	vlm.d = &valuesLocMap{
		leftMask:   vlm.leftMask >> 1,
		a:          b,
		cores:      vlm.cores,
		splitCount: vlm.splitCount,
	}
	vlm.c = &valuesLocMap{
		leftMask:   vlm.leftMask >> 1,
		a:          a,
		cores:      vlm.cores,
		splitCount: vlm.splitCount,
	}
	vlm.a = nil
	vlm.b = nil
	vlm.resizing = false
}
<commit_msg>splitCount now bucketCount *3<commit_after>package brimstore

import (
	"os"
	"strconv"
	"sync"
	"sync/atomic"
	"unsafe"

	"github.com/gholt/brimutil"
)

const (
	_VALUESBLOCK_UNUSED   = iota
	_VALUESBLOCK_TOMB     = iota
	_VALUESBLOCK_IDOFFSET = iota
)

type valuesLocMap struct {
	leftMask   uint64
	a          *valuesLocStore
	b          *valuesLocStore
	c          *valuesLocMap
	d          *valuesLocMap
	resizing   bool
	resizeLock sync.Mutex
	cores      int
	splitCount int
}

type valuesLocStore struct {
	buckets []valueLoc
	locks   []sync.RWMutex
	used    int32
}

type valueLoc struct {
	next    *valueLoc
	keyA    uint64
	keyB    uint64
	seq     uint64
	blockID uint16
	offset  uint32
	length  uint32
}

type valuesLocMapStats struct {
	depth        uint64
	depthCounts  []uint64
	sections     uint64
	storages     uint64
	buckets      uint64
	bucketCounts []uint64
	splitCount   uint64
	locs         uint64
	pointerLocs  uint64
	unused       uint64
	tombs        uint64
	used         uint64
	length       uint64
}

func newValuesLocMap(opts *ValuesStoreOpts) *valuesLocMap {
	if opts == nil {
		opts = NewValuesStoreOpts()
	}
	cores := opts.Cores
	if cores < 1 {
		cores = 1
	}
	valuesLocMapPageSize := opts.ValuesLocMapPageSize
	if env := os.Getenv("BRIMSTORE_VALUESSTORE_VALUESLOCMAP_PAGESIZE"); env != "" {
		if val, err := strconv.Atoi(env); err == nil {
			valuesLocMapPageSize = val
		}
	}
	if valuesLocMapPageSize < 4096 {
		valuesLocMapPageSize = 4096
	}
	bucketCount := 1 << brimutil.PowerOfTwoNeeded(uint64(valuesLocMapPageSize)/uint64(unsafe.Sizeof(valueLoc{})))
	lockCount := 1 << brimutil.PowerOfTwoNeeded(uint64(cores*cores))
	if lockCount > bucketCount {
		lockCount = bucketCount
	}
	return &valuesLocMap{
		leftMask: uint64(1) << 63,
		a: &valuesLocStore{
			buckets: make([]valueLoc, bucketCount),
			locks:   make([]sync.RWMutex, lockCount),
		},
		cores:      cores,
		splitCount: bucketCount * 3,
	}
}

func (vlm *valuesLocMap) get(keyA uint64, keyB uint64) (uint64, uint16, uint32, uint32) {
	var seq uint64
	var blockID uint16 = _VALUESBLOCK_UNUSED
	var offset uint32
	var length uint32
	var a *valuesLocStore
	var b *valuesLocStore
	for {
		a = vlm.a
		b = vlm.b
		c := vlm.c
		d := vlm.d
		if c == nil {
			break
		}
		if keyA&vlm.leftMask == 0 {
			vlm = c
		} else {
			vlm = d
		}
	}
	if b != nil {
		if keyA&vlm.leftMask == 0 {
			b = nil
		} else {
			a, b = b, a
		}
	}
	bix := keyB % uint64(len(a.buckets))
	lix := bix % uint64(len(a.locks))
	a.locks[lix].RLock()
	if b != nil {
		b.locks[lix].RLock()
	}
	for itemA := &a.buckets[bix]; itemA != nil; itemA = itemA.next {
		if itemA.blockID != _VALUESBLOCK_UNUSED && itemA.keyA == keyA && itemA.keyB == keyB {
			seq, blockID, offset, length = itemA.seq, itemA.blockID, itemA.offset, itemA.length
			break
		}
	}
	if blockID == _VALUESBLOCK_UNUSED && b != nil {
		for itemB := &b.buckets[bix]; itemB != nil; itemB = itemB.next {
			if itemB.blockID != _VALUESBLOCK_UNUSED && itemB.keyA == keyA && itemB.keyB == keyB {
				seq, blockID, offset, length = itemB.seq, itemB.blockID, itemB.offset, itemB.length
				break
			}
		}
	}
	if b != nil {
		b.locks[lix].RUnlock()
	}
	a.locks[lix].RUnlock()
	return seq, blockID, offset, length
}

func (vlm *valuesLocMap) set(keyA uint64, keyB uint64, seq uint64, blockID uint16, offset uint32, length uint32, evenIfSameSeq bool) uint64 {
	var oldSeq uint64
	var a *valuesLocStore
	var b *valuesLocStore
	for {
		a = vlm.a
		b = vlm.b
		c := vlm.c
		d := vlm.d
		if c == nil {
			break
		}
		if keyA&vlm.leftMask == 0 {
			vlm = c
		} else {
			vlm = d
		}
	}
	if b != nil {
		if keyA&vlm.leftMask == 0 {
			b = nil
		} else {
			a, b = b, a
		}
	}
	bix := keyB % uint64(len(a.buckets))
	lix := bix % uint64(len(a.locks))
	done := false
	var unusedItemA *valueLoc
	a.locks[lix].Lock()
	if b != nil {
		b.locks[lix].Lock()
	}
	for itemA := &a.buckets[bix]; itemA != nil; itemA = itemA.next {
		if itemA.blockID == _VALUESBLOCK_UNUSED {
			if unusedItemA == nil {
				unusedItemA = itemA
			}
			continue
		}
		if itemA.keyA == keyA && itemA.keyB == keyB {
			oldSeq = itemA.seq
			if (evenIfSameSeq && itemA.seq == seq) || itemA.seq < seq {
				if blockID == _VALUESBLOCK_UNUSED {
					atomic.AddInt32(&a.used, -1)
				}
				itemA.seq = seq
				itemA.blockID = blockID
				itemA.offset = offset
				itemA.length = length
			}
			done = true
			break
		}
	}
	if !done && b != nil {
		for itemB := &b.buckets[bix]; itemB != nil; itemB = itemB.next {
			if itemB.blockID == _VALUESBLOCK_UNUSED {
				continue
			}
			if itemB.keyA == keyA && itemB.keyB == keyB {
				oldSeq = itemB.seq
				if (evenIfSameSeq && itemB.seq == seq) || itemB.seq < seq {
					atomic.AddInt32(&b.used, -1)
					itemB.blockID = _VALUESBLOCK_UNUSED
				} else {
					done = true
				}
				break
			}
		}
	}
	if !done && blockID != _VALUESBLOCK_UNUSED {
		atomic.AddInt32(&a.used, 1)
		if unusedItemA != nil {
			unusedItemA.keyA = keyA
			unusedItemA.keyB = keyB
			unusedItemA.seq = seq
			unusedItemA.blockID = blockID
			unusedItemA.offset = offset
			unusedItemA.length = length
		} else {
			a.buckets[bix].next = &valueLoc{
				next:    a.buckets[bix].next,
				keyA:    keyA,
				keyB:    keyB,
				seq:     seq,
				blockID: blockID,
				offset:  offset,
				length:  length,
			}
		}
	}
	if b != nil {
		b.locks[lix].Unlock()
	}
	a.locks[lix].Unlock()
	if b == nil {
		if int(atomic.LoadInt32(&a.used)) > vlm.splitCount {
			go vlm.split()
		}
	}
	return oldSeq
}

func (vlm *valuesLocMap) isResizing() bool {
	c, d := vlm.c, vlm.d
	return vlm.resizing || (c != nil && c.isResizing()) || (d != nil && d.isResizing())
}

func (vlm *valuesLocMap) gatherStats() *valuesLocMapStats {
	buckets := 0
	for llm := vlm; llm != nil; llm = llm.c {
		a := llm.a
		if a != nil {
			buckets = len(a.buckets)
			break
		}
	}
	stats := &valuesLocMapStats{
		depthCounts:  []uint64{0},
		buckets:      uint64(buckets),
		bucketCounts: make([]uint64, buckets),
		splitCount:   uint64(vlm.splitCount),
	}
	vlm.gatherStatsHelper(stats)
	stats.depthCounts = stats.depthCounts[1:]
	return stats
}

func (vlm *valuesLocMap) gatherStatsHelper(stats *valuesLocMapStats) {
	stats.sections++
	stats.depth++
	if stats.depth < uint64(len(stats.depthCounts)) {
		stats.depthCounts[stats.depth]++
	} else {
		stats.depthCounts = append(stats.depthCounts, 1)
	}
	a := vlm.a
	b := vlm.b
	c := vlm.c
	d := vlm.d
	for _, s := range []*valuesLocStore{a, b} {
		if s != nil {
			stats.storages++
			for bix := len(s.buckets) - 1; bix >= 0; bix-- {
				lix := bix % len(s.locks)
				s.locks[lix].RLock()
				for item := &s.buckets[bix]; item != nil; item = item.next {
					stats.bucketCounts[bix]++
					if item.next != nil {
						stats.pointerLocs++
					}
					stats.locs++
					switch item.blockID {
					case _VALUESBLOCK_UNUSED:
						stats.unused++
					case _VALUESBLOCK_TOMB:
						stats.tombs++
					default:
						stats.used++
						stats.length += uint64(item.length)
					}
				}
				s.locks[lix].RUnlock()
			}
		}
	}
	depthOrig := stats.depth
	if c != nil {
		c.gatherStatsHelper(stats)
		depthC := stats.depth
		stats.depth = depthOrig
		d.gatherStatsHelper(stats)
		if depthC > stats.depth {
			stats.depth = depthC
		}
	}
}

func (vlm *valuesLocMap) split() {
	if vlm.resizing {
		return
	}
	vlm.resizeLock.Lock()
	a := vlm.a
	b := vlm.b
	if vlm.resizing || a == nil || b != nil || int(atomic.LoadInt32(&a.used)) < vlm.splitCount {
		vlm.resizeLock.Unlock()
		return
	}
	vlm.resizing = true
	vlm.resizeLock.Unlock()
	b = &valuesLocStore{
		buckets: make([]valueLoc, len(a.buckets)),
		locks:   make([]sync.RWMutex, len(a.locks)),
	}
	vlm.b = b
	wg := &sync.WaitGroup{}
	wg.Add(vlm.cores)
	for core := 0; core < vlm.cores; core++ {
		go func(coreOffset int) {
			clean := false
			for !clean {
				clean = true
				for bix := len(a.buckets) - 1 - coreOffset; bix >= 0; bix -= vlm.cores {
					lix := bix % len(a.locks)
					b.locks[lix].Lock()
					a.locks[lix].Lock()
				NEXT_ITEM_A:
					for itemA := &a.buckets[bix]; itemA != nil; itemA = itemA.next {
						if itemA.blockID == _VALUESBLOCK_UNUSED || itemA.keyA&vlm.leftMask == 0 {
							continue
						}
						clean = false
						var unusedItemB *valueLoc
						for itemB := &b.buckets[bix]; itemB != nil; itemB = itemB.next {
							if itemB.blockID == _VALUESBLOCK_UNUSED {
								if unusedItemB == nil {
									unusedItemB = itemB
								}
								continue
							}
							if itemA.keyA == itemB.keyA && itemA.keyB == itemB.keyB {
								if itemA.seq > itemB.seq {
									itemB.keyA = itemA.keyA
									itemB.keyB = itemA.keyB
									itemB.seq = itemA.seq
									itemB.blockID = itemA.blockID
									itemB.offset = itemA.offset
									itemB.length = itemA.length
								}
								atomic.AddInt32(&a.used, -1)
								itemA.blockID = _VALUESBLOCK_UNUSED
								continue NEXT_ITEM_A
							}
						}
						atomic.AddInt32(&b.used, 1)
						if unusedItemB != nil {
							unusedItemB.keyA = itemA.keyA
							unusedItemB.keyB = itemA.keyB
							unusedItemB.seq = itemA.seq
							unusedItemB.blockID = itemA.blockID
							unusedItemB.offset = itemA.offset
							unusedItemB.length = itemA.length
						} else {
							b.buckets[bix].next = &valueLoc{
								next:    b.buckets[bix].next,
								keyA:    itemA.keyA,
								keyB:    itemA.keyB,
								seq:     itemA.seq,
								blockID: itemA.blockID,
								offset:  itemA.offset,
								length:  itemA.length,
							}
						}
						atomic.AddInt32(&a.used, -1)
						itemA.blockID = _VALUESBLOCK_UNUSED
					}
					a.locks[lix].Unlock()
					b.locks[lix].Unlock()
				}
			}
			wg.Done()
		}(core)
	}
	wg.Wait()
	vlm.d = &valuesLocMap{
		leftMask:   vlm.leftMask >> 1,
		a:          b,
		cores:      vlm.cores,
		splitCount: vlm.splitCount,
	}
	vlm.c = &valuesLocMap{
		leftMask:   vlm.leftMask >> 1,
		a:          a,
		cores:      vlm.cores,
		splitCount: vlm.splitCount,
	}
	vlm.a = nil
	vlm.b = nil
	vlm.resizing = false
}
<|endoftext|>


<commit_before>package vault

import (
	"context"
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"net"
	"net/http"
	"net/url"
	"runtime"
	"sync"
	"sync/atomic"
	"time"

	"github.com/hashicorp/vault/helper/forwarding"
	"golang.org/x/net/http2"
	"google.golang.org/grpc"
	"google.golang.org/grpc/keepalive"
)

const (
	clusterListenerAcceptDeadline = 500 * time.Millisecond
	heartbeatInterval             = 30 * time.Second
	requestForwardingALPN         = "req_fw_sb-act_v1"
)

// Starts the listeners and servers necessary to handle forwarded requests
func (c *Core) startForwarding() error {
	c.logger.Trace("core: cluster listener setup function")
	defer c.logger.Trace("core: leaving cluster listener setup function")

	// Clean up in case we have transitioned from a client to a server
	c.requestForwardingConnectionLock.Lock()
	c.clearForwardingClients()
	c.requestForwardingConnectionLock.Unlock()

	// Resolve locally to avoid races
	ha := c.ha != nil

	// Get our TLS config
	tlsConfig, err := c.ClusterTLSConfig()
	if err != nil {
		c.logger.Error("core: failed to get tls configuration when starting forwarding", "error", err)
		return err
	}

	// The server supports all of the possible protos
	tlsConfig.NextProtos = []string{"h2", requestForwardingALPN}

	// Create our RPC server and register the request handler server
	c.clusterParamsLock.Lock()

	if c.rpcServer != nil {
		c.logger.Warn("core: forwarding rpc server already running")
		return nil
	}

	c.rpcServer = grpc.NewServer(
		grpc.KeepaliveParams(keepalive.ServerParameters{
			Time: 2 * heartbeatInterval,
		}),
	)

	if ha && c.clusterHandler != nil {
		RegisterRequestForwardingServer(c.rpcServer, &forwardedRequestRPCServer{
			core:    c,
			handler: c.clusterHandler,
		})
	}
	c.clusterParamsLock.Unlock()

	// Create the HTTP/2 server that will be shared by both RPC and regular
	// duties. Doing it this way instead of listening via the server and gRPC
	// allows us to re-use the same port via ALPN. We can just tell the server
	// to serve a given conn and which handler to use.
	fws := &http2.Server{}

	// Shutdown coordination logic
	var shutdown uint32
	shutdownWg := &sync.WaitGroup{}

	for _, addr := range c.clusterListenerAddrs {
		shutdownWg.Add(1)

		// Force a local resolution to avoid data races
		laddr := addr

		// Start our listening loop
		go func() {
			defer shutdownWg.Done()

			if c.logger.IsInfo() {
				c.logger.Info("core/startClusterListener: starting listener", "listener_address", laddr)
			}

			// Create a TCP listener. We do this separately and specifically
			// with TCP so that we can set deadlines.
			tcpLn, err := net.ListenTCP("tcp", laddr)
			if err != nil {
				c.logger.Error("core/startClusterListener: error starting listener", "error", err)
				return
			}

			// Wrap the listener with TLS
			tlsLn := tls.NewListener(tcpLn, tlsConfig)
			defer tlsLn.Close()

			if c.logger.IsInfo() {
				c.logger.Info("core/startClusterListener: serving cluster requests", "cluster_listen_address", tlsLn.Addr())
			}

			for {
				if atomic.LoadUint32(&shutdown) > 0 {
					return
				}

				// Set the deadline for the accept call. If it passes we'll get
				// an error, causing us to check the condition at the top
				// again.
				tcpLn.SetDeadline(time.Now().Add(clusterListenerAcceptDeadline))

				// Accept the connection
				conn, err := tlsLn.Accept()
				if err != nil {
					if err, ok := err.(net.Error); ok && !err.Timeout() {
						c.logger.Debug("core: non-timeout error accepting on cluster port", "error", err)
					}
					if conn != nil {
						conn.Close()
					}
					continue
				}
				if conn == nil {
					continue
				}
				defer conn.Close()

				// Type assert to TLS connection and handshake to populate the
				// connection state
				tlsConn := conn.(*tls.Conn)
				err = tlsConn.Handshake()
				if err != nil {
					if c.logger.IsDebug() {
						c.logger.Debug("core: error handshaking cluster connection", "error", err)
					}
					tlsConn.Close()
					continue
				}

				switch tlsConn.ConnectionState().NegotiatedProtocol {
				case requestForwardingALPN:
					if !ha {
						tlsConn.Close()
						continue
					}

					c.logger.Trace("core: got request forwarding connection")
					c.clusterParamsLock.RLock()
					rpcServer := c.rpcServer
					c.clusterParamsLock.RUnlock()
					go func() {
						fws.ServeConn(tlsConn, &http2.ServeConnOpts{
							Handler: rpcServer,
						})
						tlsConn.Close()
					}()

				default:
					c.logger.Debug("core: unknown negotiated protocol on cluster port")
					tlsConn.Close()
					continue
				}
			}
		}()
	}

	// This is in its own goroutine so that we don't block the main thread, and
	// thus we use atomic and channels to coordinate
	// However, because you can't query the status of a channel, we set a bool
	// here while we have the state lock to know whether to actually send a
	// shutdown (e.g. whether the channel will block). See issue #2083.
	c.clusterListenersRunning = true
	go func() {
		// If we get told to shut down...
		<-c.clusterListenerShutdownCh

		// Stop the RPC server
		c.logger.Info("core: shutting down forwarding rpc listeners")
		c.clusterParamsLock.Lock()
		c.rpcServer.Stop()
		c.rpcServer = nil
		c.clusterParamsLock.Unlock()
		c.logger.Info("core: forwarding rpc listeners stopped")

		// Set the shutdown flag. This will cause the listeners to shut down
		// within the deadline in clusterListenerAcceptDeadline
		atomic.StoreUint32(&shutdown, 1)

		// Wait for them all to shut down
		shutdownWg.Wait()
		c.logger.Info("core: rpc listeners successfully shut down")

		// Tell the main thread that shutdown is done.
		c.clusterListenerShutdownSuccessCh <- struct{}{}
	}()

	return nil
}

// refreshRequestForwardingConnection ensures that the client/transport are
// alive and that the current active address value matches the most
// recently-known address.
func (c *Core) refreshRequestForwardingConnection(clusterAddr string) error {
	c.logger.Trace("core: refreshing forwarding connection")
	defer c.logger.Trace("core: done refreshing forwarding connection")

	c.requestForwardingConnectionLock.Lock()
	defer c.requestForwardingConnectionLock.Unlock()

	// Clean things up first
	c.clearForwardingClients()

	// If we don't have anything to connect to, just return
	if clusterAddr == "" {
		return nil
	}

	clusterURL, err := url.Parse(clusterAddr)
	if err != nil {
		c.logger.Error("core: error parsing cluster address attempting to refresh forwarding connection", "error", err)
		return err
	}

	// Set up grpc forwarding handling
	// It's not really insecure, but we have to dial manually to get the
	// ALPN header right. It's just "insecure" because GRPC isn't managing
	// the TLS state.
	ctx, cancelFunc := context.WithCancel(context.Background())
	c.rpcClientConn, err = grpc.DialContext(ctx, clusterURL.Host,
		grpc.WithDialer(c.getGRPCDialer(requestForwardingALPN, "", nil)),
		grpc.WithInsecure(), // it's not, we handle it in the dialer
		grpc.WithKeepaliveParams(keepalive.ClientParameters{
			Time: 2 * heartbeatInterval,
		}))
	if err != nil {
		cancelFunc()
		c.logger.Error("core: err setting up forwarding rpc client", "error", err)
		return err
	}
	c.rpcClientConnContext = ctx
	c.rpcClientConnCancelFunc = cancelFunc
	c.rpcForwardingClient = &forwardingClient{
		RequestForwardingClient: NewRequestForwardingClient(c.rpcClientConn),
		core:        c,
		echoTicker:  time.NewTicker(heartbeatInterval),
		echoContext: ctx,
	}
	c.rpcForwardingClient.startHeartbeat()

	return nil
}

func (c *Core) clearForwardingClients() {
	c.logger.Trace("core: clearing forwarding clients")
	defer c.logger.Trace("core: done clearing forwarding clients")

	if c.rpcClientConnCancelFunc != nil {
		c.rpcClientConnCancelFunc()
		c.rpcClientConnCancelFunc = nil
	}
	if c.rpcClientConn != nil {
		c.rpcClientConn.Close()
		c.rpcClientConn = nil
	}

	c.rpcClientConnContext = nil
	c.rpcForwardingClient = nil
}

// ForwardRequest forwards a given request to the active node and returns the
// response.
func (c *Core) ForwardRequest(req *http.Request) (int, http.Header, []byte, error) {
	c.requestForwardingConnectionLock.RLock()
	defer c.requestForwardingConnectionLock.RUnlock()

	if c.rpcForwardingClient == nil {
		return 0, nil, nil, ErrCannotForward
	}

	freq, err := forwarding.GenerateForwardedRequest(req)
	if err != nil {
		c.logger.Error("core: error creating forwarding RPC request", "error", err)
		return 0, nil, nil, fmt.Errorf("error creating forwarding RPC request")
	}
	if freq == nil {
		c.logger.Error("core: got nil forwarding RPC request")
		return 0, nil, nil, fmt.Errorf("got nil forwarding RPC request")
	}
	resp, err := c.rpcForwardingClient.ForwardRequest(c.rpcClientConnContext, freq)
	if err != nil {
		c.logger.Error("core: error during forwarded RPC request", "error", err)
		return 0, nil, nil, fmt.Errorf("error during forwarding RPC request")
	}

	var header http.Header
	if resp.HeaderEntries != nil {
		header = make(http.Header)
		for k, v := range resp.HeaderEntries {
			header[k] = v.Values
		}
	}

	return int(resp.StatusCode), header, resp.Body, nil
}

// getGRPCDialer is used to return a dialer that has the correct TLS
// configuration. Otherwise gRPC tries to be helpful and stomps all over our
// NextProtos.
func (c *Core) getGRPCDialer(alpnProto, serverName string, caCert *x509.Certificate) func(string, time.Duration) (net.Conn, error) {
	return func(addr string, timeout time.Duration) (net.Conn, error) {
		tlsConfig, err := c.ClusterTLSConfig()
		if err != nil {
			c.logger.Error("core: failed to get tls configuration", "error", err)
			return nil, err
		}
		if serverName != "" {
			tlsConfig.ServerName = serverName
		}
		if caCert != nil {
			pool := x509.NewCertPool()
			pool.AddCert(caCert)
			tlsConfig.RootCAs = pool
			tlsConfig.ClientCAs = pool
		}
		c.logger.Trace("core: creating rpc dialer", "host", tlsConfig.ServerName)

		tlsConfig.NextProtos = []string{alpnProto}
		dialer := &net.Dialer{
			Timeout: timeout,
		}
		return tls.DialWithDialer(dialer, "tcp", addr, tlsConfig)
	}
}

type forwardedRequestRPCServer struct {
	core    *Core
	handler http.Handler
}

func (s *forwardedRequestRPCServer) ForwardRequest(ctx context.Context, freq *forwarding.Request) (*forwarding.Response, error) {
	//s.core.logger.Trace("forwarding: serving rpc forwarded request")

	// Parse an http.Request out of it
	req, err := forwarding.ParseForwardedRequest(freq)
	if err != nil {
		return nil, err
	}

	// A very dummy response writer that doesn't follow normal semantics, just
	// lets you write a status code (last written wins) and a body. But it
	// meets the interface requirements.
	w := forwarding.NewRPCResponseWriter()

	resp := &forwarding.Response{}

	runRequest := func() {
		defer func() {
			// Logic here comes mostly from the Go source code
			if err := recover(); err != nil {
				const size = 64 << 10
				buf := make([]byte, size)
				buf = buf[:runtime.Stack(buf, false)]
				s.core.logger.Error("forwarding: panic serving request", "path", req.URL.Path, "error", err, "stacktrace", buf)
			}
		}()
		s.handler.ServeHTTP(w, req)
	}
	runRequest()
	resp.StatusCode = uint32(w.StatusCode())
	resp.Body = w.Body().Bytes()

	header := w.Header()
	if header != nil {
		resp.HeaderEntries = make(map[string]*forwarding.HeaderEntry, len(header))
		for k, v := range header {
			resp.HeaderEntries[k] = &forwarding.HeaderEntry{
				Values: v,
			}
		}
	}

	return resp, nil
}

func (s *forwardedRequestRPCServer) Echo(ctx context.Context, in *EchoRequest) (*EchoReply, error) {
	if in.ClusterAddr != "" {
		s.core.clusterPeerClusterAddrsCache.Set(in.ClusterAddr, nil, 0)
	}
	return &EchoReply{
		Message: "pong",
	}, nil
}

type forwardingClient struct {
	RequestForwardingClient

	core *Core

	echoTicker  *time.Ticker
	echoContext context.Context
}

// NOTE: we also take advantage of gRPC's keepalive bits, but as we send data
// with these requests it's useful to keep this as well
func (c *forwardingClient) startHeartbeat() {
	go func() {
		tick := func() {
			c.core.stateLock.RLock()
			clusterAddr := c.core.clusterAddr
			c.core.stateLock.RUnlock()

			ctx, cancel := context.WithTimeout(c.echoContext, 2*time.Second)
			resp, err := c.RequestForwardingClient.Echo(ctx, &EchoRequest{
				Message:     "ping",
				ClusterAddr: clusterAddr,
			})
			cancel()
			if err != nil {
				c.core.logger.Debug("forwarding: error sending echo request to active node", "error", err)
				return
			}
			if resp == nil {
				c.core.logger.Debug("forwarding: empty echo response from active node")
				return
			}
			if resp.Message != "pong" {
				c.core.logger.Debug("forwarding: unexpected echo response from active node", "message", resp.Message)
				return
			}
			c.core.logger.Trace("forwarding: successful heartbeat")
		}

		tick()

		for {
			select {
			case <-c.echoContext.Done():
				c.echoTicker.Stop()
				c.core.logger.Trace("forwarding: stopping heartbeating")
				return
			case <-c.echoTicker.C:
				tick()
			}
		}
	}()
}
<commit_msg>Fix leaking connections on cluster port (#3680)<commit_after>package vault

import (
	"context"
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"net"
	"net/http"
	"net/url"
	"runtime"
	"sync"
	"sync/atomic"
	"time"

	"github.com/hashicorp/vault/helper/forwarding"
	"golang.org/x/net/http2"
	"google.golang.org/grpc"
	"google.golang.org/grpc/keepalive"
)

const (
	clusterListenerAcceptDeadline = 500 * time.Millisecond
	heartbeatInterval             = 30 * time.Second
	requestForwardingALPN         = "req_fw_sb-act_v1"
)

// Starts the listeners and servers necessary to handle forwarded requests
func (c *Core) startForwarding() error {
	c.logger.Trace("core: cluster listener setup function")
	defer c.logger.Trace("core: leaving cluster listener setup function")

	// Clean up in case we have transitioned from a client to a server
	c.requestForwardingConnectionLock.Lock()
	c.clearForwardingClients()
	c.requestForwardingConnectionLock.Unlock()

	// Resolve locally to avoid races
	ha := c.ha != nil

	// Get our TLS config
	tlsConfig, err := c.ClusterTLSConfig()
	if err != nil {
		c.logger.Error("core: failed to get tls configuration when starting forwarding", "error", err)
		return err
	}

	// The server supports all of the possible protos
	tlsConfig.NextProtos = []string{"h2", requestForwardingALPN}

	// Create our RPC server and register the request handler server
	c.clusterParamsLock.Lock()

	if c.rpcServer != nil {
		c.logger.Warn("core: forwarding rpc server already running")
		return nil
	}

	c.rpcServer = grpc.NewServer(
		grpc.KeepaliveParams(keepalive.ServerParameters{
			Time: 2 * heartbeatInterval,
		}),
	)

	if ha && c.clusterHandler != nil {
		RegisterRequestForwardingServer(c.rpcServer, &forwardedRequestRPCServer{
			core:    c,
			handler: c.clusterHandler,
		})
	}
	c.clusterParamsLock.Unlock()

	// Create the HTTP/2 server that will be shared by both RPC and regular
	// duties. Doing it this way instead of listening via the server and gRPC
	// allows us to re-use the same port via ALPN. We can just tell the server
	// to serve a given conn and which handler to use.
	fws := &http2.Server{}

	// Shutdown coordination logic
	var shutdown uint32
	shutdownWg := &sync.WaitGroup{}

	for _, addr := range c.clusterListenerAddrs {
		shutdownWg.Add(1)

		// Force a local resolution to avoid data races
		laddr := addr

		// Start our listening loop
		go func() {
			defer shutdownWg.Done()

			// closeCh is used to shutdown the spawned goroutines once this
			// function returns
			closeCh := make(chan struct{})
			defer func() {
				close(closeCh)
			}()

			if c.logger.IsInfo() {
				c.logger.Info("core/startClusterListener: starting listener", "listener_address", laddr)
			}

			// Create a TCP listener. We do this separately and specifically
			// with TCP so that we can set deadlines.
			tcpLn, err := net.ListenTCP("tcp", laddr)
			if err != nil {
				c.logger.Error("core/startClusterListener: error starting listener", "error", err)
				return
			}

			// Wrap the listener with TLS
			tlsLn := tls.NewListener(tcpLn, tlsConfig)
			defer tlsLn.Close()

			if c.logger.IsInfo() {
				c.logger.Info("core/startClusterListener: serving cluster requests", "cluster_listen_address", tlsLn.Addr())
			}

			for {
				if atomic.LoadUint32(&shutdown) > 0 {
					return
				}

				// Set the deadline for the accept call. If it passes we'll get
				// an error, causing us to check the condition at the top
				// again.
				tcpLn.SetDeadline(time.Now().Add(clusterListenerAcceptDeadline))

				// Accept the connection
				conn, err := tlsLn.Accept()
				if err != nil {
					if err, ok := err.(net.Error); ok && !err.Timeout() {
						c.logger.Debug("core: non-timeout error accepting on cluster port", "error", err)
					}
					if conn != nil {
						conn.Close()
					}
					continue
				}
				if conn == nil {
					continue
				}

				// Type assert to TLS connection and handshake to populate the
				// connection state
				tlsConn := conn.(*tls.Conn)
				err = tlsConn.Handshake()
				if err != nil {
					if c.logger.IsDebug() {
						c.logger.Debug("core: error handshaking cluster connection", "error", err)
					}
					tlsConn.Close()
					continue
				}

				switch tlsConn.ConnectionState().NegotiatedProtocol {
				case requestForwardingALPN:
					if !ha {
						tlsConn.Close()
						continue
					}

					c.logger.Trace("core: got request forwarding connection")
					c.clusterParamsLock.RLock()
					rpcServer := c.rpcServer
					c.clusterParamsLock.RUnlock()

					shutdownWg.Add(2)
					// quitCh is used to close the connection and the second
					// goroutine if the server closes before closeCh.
					quitCh := make(chan struct{})
					go func() {
						select {
						case <-quitCh:
						case <-closeCh:
						}
						tlsConn.Close()
						shutdownWg.Done()
					}()

					go func() {
						fws.ServeConn(tlsConn, &http2.ServeConnOpts{
							Handler: rpcServer,
						})
						// close the quitCh which will close the connection and
						// the other goroutine.
						close(quitCh)
						shutdownWg.Done()
					}()

				default:
					c.logger.Debug("core: unknown negotiated protocol on cluster port")
					tlsConn.Close()
					continue
				}
			}
		}()
	}

	// This is in its own goroutine so that we don't block the main thread, and
	// thus we use atomic and channels to coordinate
	// However, because you can't query the status of a channel, we set a bool
	// here while we have the state lock to know whether to actually send a
	// shutdown (e.g. whether the channel will block). See issue #2083.
	c.clusterListenersRunning = true
	go func() {
		// If we get told to shut down...
		<-c.clusterListenerShutdownCh

		// Stop the RPC server
		c.logger.Info("core: shutting down forwarding rpc listeners")
		c.clusterParamsLock.Lock()
		c.rpcServer.Stop()
		c.rpcServer = nil
		c.clusterParamsLock.Unlock()
		c.logger.Info("core: forwarding rpc listeners stopped")

		// Set the shutdown flag. This will cause the listeners to shut down
		// within the deadline in clusterListenerAcceptDeadline
		atomic.StoreUint32(&shutdown, 1)

		// Wait for them all to shut down
		shutdownWg.Wait()
		c.logger.Info("core: rpc listeners successfully shut down")

		// Tell the main thread that shutdown is done.
		c.clusterListenerShutdownSuccessCh <- struct{}{}
	}()

	return nil
}

// refreshRequestForwardingConnection ensures that the client/transport are
// alive and that the current active address value matches the most
// recently-known address.
func (c *Core) refreshRequestForwardingConnection(clusterAddr string) error {
	c.logger.Trace("core: refreshing forwarding connection")
	defer c.logger.Trace("core: done refreshing forwarding connection")

	c.requestForwardingConnectionLock.Lock()
	defer c.requestForwardingConnectionLock.Unlock()

	// Clean things up first
	c.clearForwardingClients()

	// If we don't have anything to connect to, just return
	if clusterAddr == "" {
		return nil
	}

	clusterURL, err := url.Parse(clusterAddr)
	if err != nil {
		c.logger.Error("core: error parsing cluster address attempting to refresh forwarding connection", "error", err)
		return err
	}

	// Set up grpc forwarding handling
	// It's not really insecure, but we have to dial manually to get the
	// ALPN header right. It's just "insecure" because GRPC isn't managing
	// the TLS state.
	ctx, cancelFunc := context.WithCancel(context.Background())
	c.rpcClientConn, err = grpc.DialContext(ctx, clusterURL.Host,
		grpc.WithDialer(c.getGRPCDialer(requestForwardingALPN, "", nil)),
		grpc.WithInsecure(), // it's not, we handle it in the dialer
		grpc.WithKeepaliveParams(keepalive.ClientParameters{
			Time: 2 * heartbeatInterval,
		}))
	if err != nil {
		cancelFunc()
		c.logger.Error("core: err setting up forwarding rpc client", "error", err)
		return err
	}
	c.rpcClientConnContext = ctx
	c.rpcClientConnCancelFunc = cancelFunc
	c.rpcForwardingClient = &forwardingClient{
		RequestForwardingClient: NewRequestForwardingClient(c.rpcClientConn),
		core:        c,
		echoTicker:  time.NewTicker(heartbeatInterval),
		echoContext: ctx,
	}
	c.rpcForwardingClient.startHeartbeat()

	return nil
}

func (c *Core) clearForwardingClients() {
	c.logger.Trace("core: clearing forwarding clients")
	defer c.logger.Trace("core: done clearing forwarding clients")

	if c.rpcClientConnCancelFunc != nil {
		c.rpcClientConnCancelFunc()
		c.rpcClientConnCancelFunc = nil
	}
	if c.rpcClientConn != nil {
		c.rpcClientConn.Close()
		c.rpcClientConn = nil
	}

	c.rpcClientConnContext = nil
	c.rpcForwardingClient = nil
}

// ForwardRequest forwards a given request to the active node and returns the
// response.
func (c *Core) ForwardRequest(req *http.Request) (int, http.Header, []byte, error) {
	c.requestForwardingConnectionLock.RLock()
	defer c.requestForwardingConnectionLock.RUnlock()

	if c.rpcForwardingClient == nil {
		return 0, nil, nil, ErrCannotForward
	}

	freq, err := forwarding.GenerateForwardedRequest(req)
	if err != nil {
		c.logger.Error("core: error creating forwarding RPC request", "error", err)
		return 0, nil, nil, fmt.Errorf("error creating forwarding RPC request")
	}
	if freq == nil {
		c.logger.Error("core: got nil forwarding RPC request")
		return 0, nil, nil, fmt.Errorf("got nil forwarding RPC request")
	}
	resp, err := c.rpcForwardingClient.ForwardRequest(c.rpcClientConnContext, freq)
	if err != nil {
		c.logger.Error("core: error during forwarded RPC request", "error", err)
		return 0, nil, nil, fmt.Errorf("error during forwarding RPC request")
	}

	var header http.Header
	if resp.HeaderEntries != nil {
		header = make(http.Header)
		for k, v := range resp.HeaderEntries {
			header[k] = v.Values
		}
	}

	return int(resp.StatusCode), header, resp.Body, nil
}

// getGRPCDialer is used to return a dialer that has the correct TLS
// configuration. Otherwise gRPC tries to be helpful and stomps all over our
// NextProtos.
func (c *Core) getGRPCDialer(alpnProto, serverName string, caCert *x509.Certificate) func(string, time.Duration) (net.Conn, error) {
	return func(addr string, timeout time.Duration) (net.Conn, error) {
		tlsConfig, err := c.ClusterTLSConfig()
		if err != nil {
			c.logger.Error("core: failed to get tls configuration", "error", err)
			return nil, err
		}
		if serverName != "" {
			tlsConfig.ServerName = serverName
		}
		if caCert != nil {
			pool := x509.NewCertPool()
			pool.AddCert(caCert)
			tlsConfig.RootCAs = pool
			tlsConfig.ClientCAs = pool
		}
		c.logger.Trace("core: creating rpc dialer", "host", tlsConfig.ServerName)

		tlsConfig.NextProtos = []string{alpnProto}
		dialer := &net.Dialer{
			Timeout: timeout,
		}
		return tls.DialWithDialer(dialer, "tcp", addr, tlsConfig)
	}
}

type forwardedRequestRPCServer struct {
	core    *Core
	handler http.Handler
}

func (s *forwardedRequestRPCServer) ForwardRequest(ctx context.Context, freq *forwarding.Request) (*forwarding.Response, error) {
	//s.core.logger.Trace("forwarding: serving rpc forwarded request")

	// Parse an http.Request out of it
	req, err := forwarding.ParseForwardedRequest(freq)
	if err != nil {
		return nil, err
	}

	// A very dummy response writer that doesn't follow normal semantics, just
	// lets you write a status code (last written wins) and a body. But it
	// meets the interface requirements.
	w := forwarding.NewRPCResponseWriter()

	resp := &forwarding.Response{}

	runRequest := func() {
		defer func() {
			// Logic here comes mostly from the Go source code
			if err := recover(); err != nil {
				const size = 64 << 10
				buf := make([]byte, size)
				buf = buf[:runtime.Stack(buf, false)]
				s.core.logger.Error("forwarding: panic serving request", "path", req.URL.Path, "error", err, "stacktrace", buf)
			}
		}()
		s.handler.ServeHTTP(w, req)
	}
	runRequest()
	resp.StatusCode = uint32(w.StatusCode())
	resp.Body = w.Body().Bytes()

	header := w.Header()
	if header != nil {
		resp.HeaderEntries = make(map[string]*forwarding.HeaderEntry, len(header))
		for k, v := range header {
			resp.HeaderEntries[k] = &forwarding.HeaderEntry{
				Values: v,
			}
		}
	}

	return resp, nil
}

func (s *forwardedRequestRPCServer) Echo(ctx context.Context, in *EchoRequest) (*EchoReply, error) {
	if in.ClusterAddr != "" {
		s.core.clusterPeerClusterAddrsCache.Set(in.ClusterAddr, nil, 0)
	}
	return &EchoReply{
		Message: "pong",
	}, nil
}

type forwardingClient struct {
	RequestForwardingClient

	core *Core

	echoTicker  *time.Ticker
	echoContext context.Context
}

// NOTE: we also take advantage of gRPC's keepalive bits, but as we send data
// with these requests it's useful to keep this as well
func (c *forwardingClient) startHeartbeat() {
	go func() {
		tick := func() {
			c.core.stateLock.RLock()
			clusterAddr := c.core.clusterAddr
			c.core.stateLock.RUnlock()

			ctx, cancel := context.WithTimeout(c.echoContext, 2*time.Second)
			resp, err := c.RequestForwardingClient.Echo(ctx, &EchoRequest{
				Message:     "ping",
				ClusterAddr: clusterAddr,
			})
			cancel()
			if err != nil {
				c.core.logger.Debug("forwarding: error sending echo request to active node", "error", err)
				return
			}
			if resp == nil {
				c.core.logger.Debug("forwarding: empty echo response from active node")
				return
			}
			if resp.Message != "pong" {
				c.core.logger.Debug("forwarding: unexpected echo response from active node", "message", resp.Message)
				return
			}
			c.core.logger.Trace("forwarding: successful heartbeat")
		}

		tick()

		for {
			select {
			case <-c.echoContext.Done():
				c.echoTicker.Stop()
				c.core.logger.Trace("forwarding: stopping heartbeating")
				return
			case <-c.echoTicker.C:
				tick()
			}
		}
	}()
}
<|endoftext|>


<commit_before>package main

import (
	"archive/zip"
	"bytes"
	"fmt"
	"github.com/RobinUS2/golang-jresp"
	"github.com/julienschmidt/httprouter"
	"io/ioutil"
	"net/http"
	"os"
	"path"
)

// Backup data from the server in a ZIP file
func GetBackupConfigs(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	// Logged in
	jr := jresp.NewJsonResp()
	if !authUser(r) {
		jr.Error("Not authorized")
		fmt.Fprint(w, jr.ToString(conf.Debug))
		return
	}

	// Must be admin
	usr := getUser(r)
	if !usr.HasRole("admin") {
		jr.Error("Not allowed")
		fmt.Fprint(w, jr.ToString(conf.Debug))
		return
	}

	// Create a buffer to write our archive to.
	buf := new(bytes.Buffer)

	// Create a new zip archive.
	zw := zip.NewWriter(buf)

	// Add some files to the archive.
	//TOOD create struct and add files form respective modules
	var files = []struct {
		Name string
	}{
		{conf.HomeFile("users.json")},
		{conf.HomeFile("templates.conf")},
		{conf.HomeFile("httpchecks.json")},
		{conf.GetSslCertFile()},
		{conf.GetSslPrivateKeyFile()},
		{conf.ConfFile()},
	}
	for _, file := range files {
		fileName := file.Name
		fmt.Println(file.Name)
		if _, err := os.Stat(fileName); os.IsNotExist(err) {
			continue
		}

		// Create file in zip archive
		f, err := zw.Create(path.Base(file.Name))
		if err != nil {
			jr.Error(fmt.Sprintf("Failed creating zip: %s", err))
			fmt.Fprint(w, jr.ToString(conf.Debug))
			return
		}

		// Read contents from file\
		fileB, fileE := ioutil.ReadFile(fileName)
		if fileE != nil {
			jr.Error(fmt.Sprintf("Failed creating zip: %s", fileE))
			fmt.Fprint(w, jr.ToString(conf.Debug))
			return
		}

		// Write into file
		_, err = f.Write(fileB)
		if err != nil {
			jr.Error(fmt.Sprintf("Failed creating zip: %s", err))
			fmt.Fprint(w, jr.ToString(conf.Debug))
			return
		}
	}

	// Make sure to check the error on Close.
	zw.Flush()
	err := zw.Close()
	if err != nil {
		jr.Error(fmt.Sprintf("Failed creating zip: %s", err))
		fmt.Fprint(w, jr.ToString(conf.Debug))
		return
	}

	// Set headers
	w.Header().Set("Content-Disposition", "attachment; filename=\"indispenso.zip\"")
	w.Header().Set("Content-Type", "application/zip")
	w.Header().Set("Content-Length", fmt.Sprintf("%d", len(buf.Bytes())))

	// Dump as download
	w.Write(buf.Bytes())
}
<commit_msg>add missing ldap config file to backup handler<commit_after>package main

import (
	"archive/zip"
	"bytes"
	"fmt"
	"github.com/RobinUS2/golang-jresp"
	"github.com/julienschmidt/httprouter"
	"io/ioutil"
	"net/http"
	"os"
	"path"
)

// Backup data from the server in a ZIP file
func GetBackupConfigs(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	// Logged in
	jr := jresp.NewJsonResp()
	if !authUser(r) {
		jr.Error("Not authorized")
		fmt.Fprint(w, jr.ToString(conf.Debug))
		return
	}

	// Must be admin
	usr := getUser(r)
	if !usr.HasRole("admin") {
		jr.Error("Not allowed")
		fmt.Fprint(w, jr.ToString(conf.Debug))
		return
	}

	// Create a buffer to write our archive to.
	buf := new(bytes.Buffer)

	// Create a new zip archive.
	zw := zip.NewWriter(buf)

	// Add some files to the archive.
	//TOOD create struct and add files form respective modules
	var files = []struct {
		Name string
	}{
		{conf.HomeFile("users.json")},
		{conf.HomeFile("templates.conf")},
		{conf.HomeFile("httpchecks.json")},
		{conf.GetSslCertFile()},
		{conf.GetSslPrivateKeyFile()},
		{conf.ConfFile()},
		{conf.ldapViper.ConfigFileUsed()},
	}
	for _, file := range files {
		fileName := file.Name
		fmt.Println(file.Name)
		if _, err := os.Stat(fileName); os.IsNotExist(err) {
			continue
		}

		// Create file in zip archive
		f, err := zw.Create(path.Base(file.Name))
		if err != nil {
			jr.Error(fmt.Sprintf("Failed creating zip: %s", err))
			fmt.Fprint(w, jr.ToString(conf.Debug))
			return
		}

		// Read contents from file
		fileB, fileE := ioutil.ReadFile(fileName)
		if fileE != nil {
			jr.Error(fmt.Sprintf("Failed creating zip: %s", fileE))
			fmt.Fprint(w, jr.ToString(conf.Debug))
			return
		}

		// Write into file
		_, err = f.Write(fileB)
		if err != nil {
			jr.Error(fmt.Sprintf("Failed creating zip: %s", err))
			fmt.Fprint(w, jr.ToString(conf.Debug))
			return
		}
	}

	// Make sure to check the error on Close.
	zw.Flush()
	err := zw.Close()
	if err != nil {
		jr.Error(fmt.Sprintf("Failed creating zip: %s", err))
		fmt.Fprint(w, jr.ToString(conf.Debug))
		return
	}

	// Set headers
	w.Header().Set("Content-Disposition", "attachment; filename=\"indispenso.zip\"")
	w.Header().Set("Content-Type", "application/zip")
	w.Header().Set("Content-Length", fmt.Sprintf("%d", len(buf.Bytes())))

	// Dump as download
	w.Write(buf.Bytes())
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net"
	"time"
)

func askceph(sock, cmd string) ([]byte, error) {
	b1 := make([]byte, 64)
	var b2 []byte

	// make the connection
	conn, err := net.Dial("unix", sock)
	if err != nil {
		return nil, fmt.Errorf("could not connect to sock %v: %v\n", sock, err)
	}
	defer conn.Close()

	// send command to the admin socket
	conn.SetDeadline(time.Now().Add(250 * time.Millisecond))
	_, err = conn.Write([]byte(cmd + "\000"))
	if err != nil {
		return nil, fmt.Errorf("could not write to %v: %v\n", sock, err)
	}

	// now read what we got back.
	conn.SetDeadline(time.Now().Add(250 * time.Millisecond))
	for {
		n, err := conn.Read(b1)
		if err != nil && err.Error() != "EOF" {
			return nil, fmt.Errorf("could not read from %v: %v\n", sock, err)
		}
		// since the admin-daemon closes the connection as soon as
		// it's done writing, there's no EOM to watch for. you just
		// read until there's nothing left, and then yo're done.
		if n == 0 {
			break
		}
		b2 = append(b2, b1[:n]...)
	}
	return b2, err
}
<commit_msg>folded into library<commit_after><|endoftext|>


<commit_before>package schema

import (
	"context"
	"errors"
	"fmt"
	"sort"
	"sync"

	"github.com/hashicorp/go-multierror"
	"github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema"
	"github.com/hashicorp/terraform-plugin-sdk/terraform"
)

var ReservedProviderFields = []string{
	"alias",
	"version",
}

// Provider represents a resource provider in Terraform, and properly
// implements all of the ResourceProvider API.
//
// By defining a schema for the configuration of the provider, the
// map of supporting resources, and a configuration function, the schema
// framework takes over and handles all the provider operations for you.
//
// After defining the provider structure, it is unlikely that you'll require any
// of the methods on Provider itself.
type Provider struct {
	// Schema is the schema for the configuration of this provider. If this
	// provider has no configuration, this can be omitted.
	//
	// The keys of this map are the configuration keys, and the value is
	// the schema describing the value of the configuration.
	Schema map[string]*Schema

	// ResourcesMap is the list of available resources that this provider
	// can manage, along with their Resource structure defining their
	// own schemas and CRUD operations.
	//
	// Provider automatically handles routing operations such as Apply,
	// Diff, etc. to the proper resource.
	ResourcesMap map[string]*Resource

	// DataSourcesMap is the collection of available data sources that
	// this provider implements, with a Resource instance defining
	// the schema and Read operation of each.
	//
	// Resource instances for data sources must have a Read function
	// and must *not* implement Create, Update or Delete.
	DataSourcesMap map[string]*Resource

	// ConfigureFunc is a function for configuring the provider. If the
	// provider doesn't need to be configured, this can be omitted.
	//
	// See the ConfigureFunc documentation for more information.
	ConfigureFunc ConfigureFunc

	// MetaReset is called by TestReset to reset any state stored in the meta
	// interface.  This is especially important if the StopContext is stored by
	// the provider.
	MetaReset func() error

	meta interface{}

	// a mutex is required because TestReset can directly replace the stopCtx
	stopMu        sync.Mutex
	stopCtx       context.Context
	stopCtxCancel context.CancelFunc
	stopOnce      sync.Once

	TerraformVersion string
}

// ConfigureFunc is the function used to configure a Provider.
//
// The interface{} value returned by this function is stored and passed into
// the subsequent resources as the meta parameter. This return value is
// usually used to pass along a configured API client, a configuration
// structure, etc.
type ConfigureFunc func(*ResourceData) (interface{}, error)

// InternalValidate should be called to validate the structure
// of the provider.
//
// This should be called in a unit test for any provider to verify
// before release that a provider is properly configured for use with
// this library.
func (p *Provider) InternalValidate() error {
	if p == nil {
		return errors.New("provider is nil")
	}

	var validationErrors error
	sm := schemaMap(p.Schema)
	if err := sm.InternalValidate(sm); err != nil {
		validationErrors = multierror.Append(validationErrors, err)
	}

	// Provider-specific checks
	for k, _ := range sm {
		if isReservedProviderFieldName(k) {
			return fmt.Errorf("%s is a reserved field name for a provider", k)
		}
	}

	for k, r := range p.ResourcesMap {
		if err := r.InternalValidate(nil, true); err != nil {
			validationErrors = multierror.Append(validationErrors, fmt.Errorf("resource %s: %s", k, err))
		}
	}

	for k, r := range p.DataSourcesMap {
		if err := r.InternalValidate(nil, false); err != nil {
			validationErrors = multierror.Append(validationErrors, fmt.Errorf("data source %s: %s", k, err))
		}
	}

	return validationErrors
}

func isReservedProviderFieldName(name string) bool {
	for _, reservedName := range ReservedProviderFields {
		if name == reservedName {
			return true
		}
	}
	return false
}

// Meta returns the metadata associated with this provider that was
// returned by the Configure call. It will be nil until Configure is called.
func (p *Provider) Meta() interface{} {
	return p.meta
}

// SetMeta can be used to forcefully set the Meta object of the provider.
// Note that if Configure is called the return value will override anything
// set here.
func (p *Provider) SetMeta(v interface{}) {
	p.meta = v
}

// Stopped reports whether the provider has been stopped or not.
func (p *Provider) Stopped() bool {
	ctx := p.StopContext()
	select {
	case <-ctx.Done():
		return true
	default:
		return false
	}
}

// StopCh returns a channel that is closed once the provider is stopped.
func (p *Provider) StopContext() context.Context {
	p.stopOnce.Do(p.stopInit)

	p.stopMu.Lock()
	defer p.stopMu.Unlock()

	return p.stopCtx
}

func (p *Provider) stopInit() {
	p.stopMu.Lock()
	defer p.stopMu.Unlock()

	p.stopCtx, p.stopCtxCancel = context.WithCancel(context.Background())
}

// Stop implementation of terraform.ResourceProvider interface.
func (p *Provider) Stop() error {
	p.stopOnce.Do(p.stopInit)

	p.stopMu.Lock()
	defer p.stopMu.Unlock()

	p.stopCtxCancel()
	return nil
}

// TestReset resets any state stored in the Provider, and will call TestReset
// on Meta if it implements the TestProvider interface.
// This may be used to reset the schema.Provider at the start of a test, and is
// automatically called by resource.Test.
func (p *Provider) TestReset() error {
	p.stopInit()
	if p.MetaReset != nil {
		return p.MetaReset()
	}
	return nil
}

// GetSchema implementation of terraform.ResourceProvider interface
func (p *Provider) GetSchema(req *terraform.ProviderSchemaRequest) (*terraform.ProviderSchema, error) {
	resourceTypes := map[string]*configschema.Block{}
	dataSources := map[string]*configschema.Block{}

	for _, name := range req.ResourceTypes {
		if r, exists := p.ResourcesMap[name]; exists {
			resourceTypes[name] = r.CoreConfigSchema()
		}
	}
	for _, name := range req.DataSources {
		if r, exists := p.DataSourcesMap[name]; exists {
			dataSources[name] = r.CoreConfigSchema()
		}
	}

	return &terraform.ProviderSchema{
		Provider:      schemaMap(p.Schema).CoreConfigSchema(),
		ResourceTypes: resourceTypes,
		DataSources:   dataSources,
	}, nil
}

// Input implementation of terraform.ResourceProvider interface.
func (p *Provider) Input(
	input terraform.UIInput,
	c *terraform.ResourceConfig) (*terraform.ResourceConfig, error) {
	return schemaMap(p.Schema).Input(input, c)
}

// Validate implementation of terraform.ResourceProvider interface.
func (p *Provider) Validate(c *terraform.ResourceConfig) ([]string, []error) {
	if err := p.InternalValidate(); err != nil {
		return nil, []error{fmt.Errorf(
			"Internal validation of the provider failed! This is always a bug\n"+
				"with the provider itself, and not a user issue. Please report\n"+
				"this bug:\n\n%s", err)}
	}

	return schemaMap(p.Schema).Validate(c)
}

// ValidateResource implementation of terraform.ResourceProvider interface.
func (p *Provider) ValidateResource(
	t string, c *terraform.ResourceConfig) ([]string, []error) {
	r, ok := p.ResourcesMap[t]
	if !ok {
		return nil, []error{fmt.Errorf(
			"Provider doesn't support resource: %s", t)}
	}

	return r.Validate(c)
}

// Configure implementation of terraform.ResourceProvider interface.
func (p *Provider) Configure(c *terraform.ResourceConfig) error {
	// No configuration
	if p.ConfigureFunc == nil {
		return nil
	}

	sm := schemaMap(p.Schema)

	// Get a ResourceData for this configuration. To do this, we actually
	// generate an intermediary "diff" although that is never exposed.
	diff, err := sm.Diff(nil, c, nil, p.meta, true)
	if err != nil {
		return err
	}

	data, err := sm.Data(nil, diff)
	if err != nil {
		return err
	}

	meta, err := p.ConfigureFunc(data)
	if err != nil {
		return err
	}

	p.meta = meta
	return nil
}

// Apply implementation of terraform.ResourceProvider interface.
func (p *Provider) Apply(
	info *terraform.InstanceInfo,
	s *terraform.InstanceState,
	d *terraform.InstanceDiff) (*terraform.InstanceState, error) {
	r, ok := p.ResourcesMap[info.Type]
	if !ok {
		return nil, fmt.Errorf("unknown resource type: %s", info.Type)
	}

	return r.Apply(s, d, p.meta)
}

// Diff implementation of terraform.ResourceProvider interface.
func (p *Provider) Diff(
	info *terraform.InstanceInfo,
	s *terraform.InstanceState,
	c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) {
	r, ok := p.ResourcesMap[info.Type]
	if !ok {
		return nil, fmt.Errorf("unknown resource type: %s", info.Type)
	}

	return r.Diff(s, c, p.meta)
}

// SimpleDiff is used by the new protocol wrappers to get a diff that doesn't
// attempt to calculate ignore_changes.
func (p *Provider) SimpleDiff(
	info *terraform.InstanceInfo,
	s *terraform.InstanceState,
	c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) {
	r, ok := p.ResourcesMap[info.Type]
	if !ok {
		return nil, fmt.Errorf("unknown resource type: %s", info.Type)
	}

	return r.simpleDiff(s, c, p.meta)
}

// Refresh implementation of terraform.ResourceProvider interface.
func (p *Provider) Refresh(
	info *terraform.InstanceInfo,
	s *terraform.InstanceState) (*terraform.InstanceState, error) {
	r, ok := p.ResourcesMap[info.Type]
	if !ok {
		return nil, fmt.Errorf("unknown resource type: %s", info.Type)
	}

	return r.Refresh(s, p.meta)
}

// Resources implementation of terraform.ResourceProvider interface.
func (p *Provider) Resources() []terraform.ResourceType {
	keys := make([]string, 0, len(p.ResourcesMap))
	for k := range p.ResourcesMap {
		keys = append(keys, k)
	}
	sort.Strings(keys)

	result := make([]terraform.ResourceType, 0, len(keys))
	for _, k := range keys {
		resource := p.ResourcesMap[k]

		// This isn't really possible (it'd fail InternalValidate), but
		// we do it anyways to avoid a panic.
		if resource == nil {
			resource = &Resource{}
		}

		result = append(result, terraform.ResourceType{
			Name:       k,
			Importable: resource.Importer != nil,

			// Indicates that a provider is compiled against a new enough
			// version of core to support the GetSchema method.
			SchemaAvailable: true,
		})
	}

	return result
}

func (p *Provider) ImportState(
	info *terraform.InstanceInfo,
	id string) ([]*terraform.InstanceState, error) {
	// Find the resource
	r, ok := p.ResourcesMap[info.Type]
	if !ok {
		return nil, fmt.Errorf("unknown resource type: %s", info.Type)
	}

	// If it doesn't support import, error
	if r.Importer == nil {
		return nil, fmt.Errorf("resource %s doesn't support import", info.Type)
	}

	// Create the data
	data := r.Data(nil)
	data.SetId(id)
	data.SetType(info.Type)

	// Call the import function
	results := []*ResourceData{data}
	if r.Importer.State != nil {
		var err error
		results, err = r.Importer.State(data, p.meta)
		if err != nil {
			return nil, err
		}
	}

	// Convert the results to InstanceState values and return it
	states := make([]*terraform.InstanceState, len(results))
	for i, r := range results {
		states[i] = r.State()
	}

	// Verify that all are non-nil. If there are any nil the error
	// isn't obvious so we circumvent that with a friendlier error.
	for _, s := range states {
		if s == nil {
			return nil, fmt.Errorf(
				"nil entry in ImportState results. This is always a bug with\n" +
					"the resource that is being imported. Please report this as\n" +
					"a bug to Terraform.")
		}
	}

	return states, nil
}

// ValidateDataSource implementation of terraform.ResourceProvider interface.
func (p *Provider) ValidateDataSource(
	t string, c *terraform.ResourceConfig) ([]string, []error) {
	r, ok := p.DataSourcesMap[t]
	if !ok {
		return nil, []error{fmt.Errorf(
			"Provider doesn't support data source: %s", t)}
	}

	return r.Validate(c)
}

// ReadDataDiff implementation of terraform.ResourceProvider interface.
func (p *Provider) ReadDataDiff(
	info *terraform.InstanceInfo,
	c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) {

	r, ok := p.DataSourcesMap[info.Type]
	if !ok {
		return nil, fmt.Errorf("unknown data source: %s", info.Type)
	}

	return r.Diff(nil, c, p.meta)
}

// RefreshData implementation of terraform.ResourceProvider interface.
func (p *Provider) ReadDataApply(
	info *terraform.InstanceInfo,
	d *terraform.InstanceDiff) (*terraform.InstanceState, error) {

	r, ok := p.DataSourcesMap[info.Type]
	if !ok {
		return nil, fmt.Errorf("unknown data source: %s", info.Type)
	}

	return r.ReadDataApply(d, p.meta)
}

// DataSources implementation of terraform.ResourceProvider interface.
func (p *Provider) DataSources() []terraform.DataSource {
	keys := make([]string, 0, len(p.DataSourcesMap))
	for k, _ := range p.DataSourcesMap {
		keys = append(keys, k)
	}
	sort.Strings(keys)

	result := make([]terraform.DataSource, 0, len(keys))
	for _, k := range keys {
		result = append(result, terraform.DataSource{
			Name: k,

			// Indicates that a provider is compiled against a new enough
			// version of core to support the GetSchema method.
			SchemaAvailable: true,
		})
	}

	return result
}
<commit_msg>set TerraformVersion to 0.10/0.11 designation in Configure implementation if unset<commit_after>package schema

import (
	"context"
	"errors"
	"fmt"
	"sort"
	"sync"

	"github.com/hashicorp/go-multierror"
	"github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema"
	"github.com/hashicorp/terraform-plugin-sdk/terraform"
)

var ReservedProviderFields = []string{
	"alias",
	"version",
}

// Provider represents a resource provider in Terraform, and properly
// implements all of the ResourceProvider API.
//
// By defining a schema for the configuration of the provider, the
// map of supporting resources, and a configuration function, the schema
// framework takes over and handles all the provider operations for you.
//
// After defining the provider structure, it is unlikely that you'll require any
// of the methods on Provider itself.
type Provider struct {
	// Schema is the schema for the configuration of this provider. If this
	// provider has no configuration, this can be omitted.
	//
	// The keys of this map are the configuration keys, and the value is
	// the schema describing the value of the configuration.
	Schema map[string]*Schema

	// ResourcesMap is the list of available resources that this provider
	// can manage, along with their Resource structure defining their
	// own schemas and CRUD operations.
	//
	// Provider automatically handles routing operations such as Apply,
	// Diff, etc. to the proper resource.
	ResourcesMap map[string]*Resource

	// DataSourcesMap is the collection of available data sources that
	// this provider implements, with a Resource instance defining
	// the schema and Read operation of each.
	//
	// Resource instances for data sources must have a Read function
	// and must *not* implement Create, Update or Delete.
	DataSourcesMap map[string]*Resource

	// ConfigureFunc is a function for configuring the provider. If the
	// provider doesn't need to be configured, this can be omitted.
	//
	// See the ConfigureFunc documentation for more information.
	ConfigureFunc ConfigureFunc

	// MetaReset is called by TestReset to reset any state stored in the meta
	// interface.  This is especially important if the StopContext is stored by
	// the provider.
	MetaReset func() error

	meta interface{}

	// a mutex is required because TestReset can directly replace the stopCtx
	stopMu        sync.Mutex
	stopCtx       context.Context
	stopCtxCancel context.CancelFunc
	stopOnce      sync.Once

	TerraformVersion string
}

// ConfigureFunc is the function used to configure a Provider.
//
// The interface{} value returned by this function is stored and passed into
// the subsequent resources as the meta parameter. This return value is
// usually used to pass along a configured API client, a configuration
// structure, etc.
type ConfigureFunc func(*ResourceData) (interface{}, error)

// InternalValidate should be called to validate the structure
// of the provider.
//
// This should be called in a unit test for any provider to verify
// before release that a provider is properly configured for use with
// this library.
func (p *Provider) InternalValidate() error {
	if p == nil {
		return errors.New("provider is nil")
	}

	var validationErrors error
	sm := schemaMap(p.Schema)
	if err := sm.InternalValidate(sm); err != nil {
		validationErrors = multierror.Append(validationErrors, err)
	}

	// Provider-specific checks
	for k, _ := range sm {
		if isReservedProviderFieldName(k) {
			return fmt.Errorf("%s is a reserved field name for a provider", k)
		}
	}

	for k, r := range p.ResourcesMap {
		if err := r.InternalValidate(nil, true); err != nil {
			validationErrors = multierror.Append(validationErrors, fmt.Errorf("resource %s: %s", k, err))
		}
	}

	for k, r := range p.DataSourcesMap {
		if err := r.InternalValidate(nil, false); err != nil {
			validationErrors = multierror.Append(validationErrors, fmt.Errorf("data source %s: %s", k, err))
		}
	}

	return validationErrors
}

func isReservedProviderFieldName(name string) bool {
	for _, reservedName := range ReservedProviderFields {
		if name == reservedName {
			return true
		}
	}
	return false
}

// Meta returns the metadata associated with this provider that was
// returned by the Configure call. It will be nil until Configure is called.
func (p *Provider) Meta() interface{} {
	return p.meta
}

// SetMeta can be used to forcefully set the Meta object of the provider.
// Note that if Configure is called the return value will override anything
// set here.
func (p *Provider) SetMeta(v interface{}) {
	p.meta = v
}

// Stopped reports whether the provider has been stopped or not.
func (p *Provider) Stopped() bool {
	ctx := p.StopContext()
	select {
	case <-ctx.Done():
		return true
	default:
		return false
	}
}

// StopCh returns a channel that is closed once the provider is stopped.
func (p *Provider) StopContext() context.Context {
	p.stopOnce.Do(p.stopInit)

	p.stopMu.Lock()
	defer p.stopMu.Unlock()

	return p.stopCtx
}

func (p *Provider) stopInit() {
	p.stopMu.Lock()
	defer p.stopMu.Unlock()

	p.stopCtx, p.stopCtxCancel = context.WithCancel(context.Background())
}

// Stop implementation of terraform.ResourceProvider interface.
func (p *Provider) Stop() error {
	p.stopOnce.Do(p.stopInit)

	p.stopMu.Lock()
	defer p.stopMu.Unlock()

	p.stopCtxCancel()
	return nil
}

// TestReset resets any state stored in the Provider, and will call TestReset
// on Meta if it implements the TestProvider interface.
// This may be used to reset the schema.Provider at the start of a test, and is
// automatically called by resource.Test.
func (p *Provider) TestReset() error {
	p.stopInit()
	if p.MetaReset != nil {
		return p.MetaReset()
	}
	return nil
}

// GetSchema implementation of terraform.ResourceProvider interface
func (p *Provider) GetSchema(req *terraform.ProviderSchemaRequest) (*terraform.ProviderSchema, error) {
	resourceTypes := map[string]*configschema.Block{}
	dataSources := map[string]*configschema.Block{}

	for _, name := range req.ResourceTypes {
		if r, exists := p.ResourcesMap[name]; exists {
			resourceTypes[name] = r.CoreConfigSchema()
		}
	}
	for _, name := range req.DataSources {
		if r, exists := p.DataSourcesMap[name]; exists {
			dataSources[name] = r.CoreConfigSchema()
		}
	}

	return &terraform.ProviderSchema{
		Provider:      schemaMap(p.Schema).CoreConfigSchema(),
		ResourceTypes: resourceTypes,
		DataSources:   dataSources,
	}, nil
}

// Input implementation of terraform.ResourceProvider interface.
func (p *Provider) Input(
	input terraform.UIInput,
	c *terraform.ResourceConfig) (*terraform.ResourceConfig, error) {
	return schemaMap(p.Schema).Input(input, c)
}

// Validate implementation of terraform.ResourceProvider interface.
func (p *Provider) Validate(c *terraform.ResourceConfig) ([]string, []error) {
	if err := p.InternalValidate(); err != nil {
		return nil, []error{fmt.Errorf(
			"Internal validation of the provider failed! This is always a bug\n"+
				"with the provider itself, and not a user issue. Please report\n"+
				"this bug:\n\n%s", err)}
	}

	return schemaMap(p.Schema).Validate(c)
}

// ValidateResource implementation of terraform.ResourceProvider interface.
func (p *Provider) ValidateResource(
	t string, c *terraform.ResourceConfig) ([]string, []error) {
	r, ok := p.ResourcesMap[t]
	if !ok {
		return nil, []error{fmt.Errorf(
			"Provider doesn't support resource: %s", t)}
	}

	return r.Validate(c)
}

// Configure implementation of terraform.ResourceProvider interface.
func (p *Provider) Configure(c *terraform.ResourceConfig) error {
	// No configuration
	if p.ConfigureFunc == nil {
		return nil
	}

	sm := schemaMap(p.Schema)

	// Get a ResourceData for this configuration. To do this, we actually
	// generate an intermediary "diff" although that is never exposed.
	diff, err := sm.Diff(nil, c, nil, p.meta, true)
	if err != nil {
		return err
	}

	data, err := sm.Data(nil, diff)
	if err != nil {
		return err
	}

	if p.TerraformVersion == "" {
		// Terraform 0.12 introduced this field to the protocol
		// We can therefore assume that if it's unconfigured at this point, it's 0.10 or 0.11
		p.TerraformVersion = "0.11+compatible"
	}
	meta, err := p.ConfigureFunc(data)
	if err != nil {
		return err
	}

	p.meta = meta
	return nil
}

// Apply implementation of terraform.ResourceProvider interface.
func (p *Provider) Apply(
	info *terraform.InstanceInfo,
	s *terraform.InstanceState,
	d *terraform.InstanceDiff) (*terraform.InstanceState, error) {
	r, ok := p.ResourcesMap[info.Type]
	if !ok {
		return nil, fmt.Errorf("unknown resource type: %s", info.Type)
	}

	return r.Apply(s, d, p.meta)
}

// Diff implementation of terraform.ResourceProvider interface.
func (p *Provider) Diff(
	info *terraform.InstanceInfo,
	s *terraform.InstanceState,
	c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) {
	r, ok := p.ResourcesMap[info.Type]
	if !ok {
		return nil, fmt.Errorf("unknown resource type: %s", info.Type)
	}

	return r.Diff(s, c, p.meta)
}

// SimpleDiff is used by the new protocol wrappers to get a diff that doesn't
// attempt to calculate ignore_changes.
func (p *Provider) SimpleDiff(
	info *terraform.InstanceInfo,
	s *terraform.InstanceState,
	c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) {
	r, ok := p.ResourcesMap[info.Type]
	if !ok {
		return nil, fmt.Errorf("unknown resource type: %s", info.Type)
	}

	return r.simpleDiff(s, c, p.meta)
}

// Refresh implementation of terraform.ResourceProvider interface.
func (p *Provider) Refresh(
	info *terraform.InstanceInfo,
	s *terraform.InstanceState) (*terraform.InstanceState, error) {
	r, ok := p.ResourcesMap[info.Type]
	if !ok {
		return nil, fmt.Errorf("unknown resource type: %s", info.Type)
	}

	return r.Refresh(s, p.meta)
}

// Resources implementation of terraform.ResourceProvider interface.
func (p *Provider) Resources() []terraform.ResourceType {
	keys := make([]string, 0, len(p.ResourcesMap))
	for k := range p.ResourcesMap {
		keys = append(keys, k)
	}
	sort.Strings(keys)

	result := make([]terraform.ResourceType, 0, len(keys))
	for _, k := range keys {
		resource := p.ResourcesMap[k]

		// This isn't really possible (it'd fail InternalValidate), but
		// we do it anyways to avoid a panic.
		if resource == nil {
			resource = &Resource{}
		}

		result = append(result, terraform.ResourceType{
			Name:       k,
			Importable: resource.Importer != nil,

			// Indicates that a provider is compiled against a new enough
			// version of core to support the GetSchema method.
			SchemaAvailable: true,
		})
	}

	return result
}

func (p *Provider) ImportState(
	info *terraform.InstanceInfo,
	id string) ([]*terraform.InstanceState, error) {
	// Find the resource
	r, ok := p.ResourcesMap[info.Type]
	if !ok {
		return nil, fmt.Errorf("unknown resource type: %s", info.Type)
	}

	// If it doesn't support import, error
	if r.Importer == nil {
		return nil, fmt.Errorf("resource %s doesn't support import", info.Type)
	}

	// Create the data
	data := r.Data(nil)
	data.SetId(id)
	data.SetType(info.Type)

	// Call the import function
	results := []*ResourceData{data}
	if r.Importer.State != nil {
		var err error
		results, err = r.Importer.State(data, p.meta)
		if err != nil {
			return nil, err
		}
	}

	// Convert the results to InstanceState values and return it
	states := make([]*terraform.InstanceState, len(results))
	for i, r := range results {
		states[i] = r.State()
	}

	// Verify that all are non-nil. If there are any nil the error
	// isn't obvious so we circumvent that with a friendlier error.
	for _, s := range states {
		if s == nil {
			return nil, fmt.Errorf(
				"nil entry in ImportState results. This is always a bug with\n" +
					"the resource that is being imported. Please report this as\n" +
					"a bug to Terraform.")
		}
	}

	return states, nil
}

// ValidateDataSource implementation of terraform.ResourceProvider interface.
func (p *Provider) ValidateDataSource(
	t string, c *terraform.ResourceConfig) ([]string, []error) {
	r, ok := p.DataSourcesMap[t]
	if !ok {
		return nil, []error{fmt.Errorf(
			"Provider doesn't support data source: %s", t)}
	}

	return r.Validate(c)
}

// ReadDataDiff implementation of terraform.ResourceProvider interface.
func (p *Provider) ReadDataDiff(
	info *terraform.InstanceInfo,
	c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) {

	r, ok := p.DataSourcesMap[info.Type]
	if !ok {
		return nil, fmt.Errorf("unknown data source: %s", info.Type)
	}

	return r.Diff(nil, c, p.meta)
}

// RefreshData implementation of terraform.ResourceProvider interface.
func (p *Provider) ReadDataApply(
	info *terraform.InstanceInfo,
	d *terraform.InstanceDiff) (*terraform.InstanceState, error) {

	r, ok := p.DataSourcesMap[info.Type]
	if !ok {
		return nil, fmt.Errorf("unknown data source: %s", info.Type)
	}

	return r.ReadDataApply(d, p.meta)
}

// DataSources implementation of terraform.ResourceProvider interface.
func (p *Provider) DataSources() []terraform.DataSource {
	keys := make([]string, 0, len(p.DataSourcesMap))
	for k, _ := range p.DataSourcesMap {
		keys = append(keys, k)
	}
	sort.Strings(keys)

	result := make([]terraform.DataSource, 0, len(keys))
	for _, k := range keys {
		result = append(result, terraform.DataSource{
			Name: k,

			// Indicates that a provider is compiled against a new enough
			// version of core to support the GetSchema method.
			SchemaAvailable: true,
		})
	}

	return result
}
<|endoftext|>