<commit_before>package dpn_test

import (
	"fmt"
	"github.com/APTrust/exchange/context"
	"github.com/APTrust/exchange/dpn"
	"github.com/APTrust/exchange/models"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"net/http"
	"path/filepath"
	"testing"
	"time"
)

// var skipSyncMessagePrinted = false

// const (
//	BAG_COUNT     = 1
//	REPL_COUNT    = 4
//	RESTORE_COUNT = 4
// )

// func loadConfig(t *testing.T) (*models.Config) {
//	configFile := filepath.Join("config", "test.json")
//	config, err := models.LoadConfigFile(configFile)
//	require.Nil(t, err)
//	return config
// }

// func runSyncTests(t *testing.T) bool {
//	config := loadConfig(t)
//	_, err := http.Get(config.DPN.RestClient.LocalServiceURL)
//	if !canRunSyncTests("aptrust", config.DPN.RestClient.LocalServiceURL, err) {
//		return false
//	}
//	for nodeNamespace, url := range config.DPN.RemoteNodeURLs {
//		if url == "" {
//			continue
//		}
//		_, err := http.Get(url)
//		if !canRunSyncTests(nodeNamespace, url, err) {
//			return false
//		}
//	}
//	return true
// }

// func canRunSyncTests(nodeNamespace string, url string, err error) (bool) {
//	if err != nil {
//		if skipSyncMessagePrinted == false {
//			skipSyncMessagePrinted = true
//			fmt.Printf("**** Skipping DPN sync integration tests: "+
//				"%s server is not running at %s\n", nodeNamespace, url)
//			fmt.Println("     Run the run_cluster.sh script in " +
//				"DPN-REST/dpnode to get a local cluster running.")
//		}
//		return false
//	}
//	return true
// }

// func newDPNSync(t *testing.T) (*dpn.DPNSync) {
//	config := loadConfig(t)
//	_context := context.NewContext(config)
//	dpnSync, err := dpn.NewDPNSync(_context)
//	require.Nil(t, err)
//	for namespace, _ := range config.DPN.RemoteNodeTokens {
//		require.NotNil(t, dpnSync.RemoteClients[namespace], namespace)
//	}
//	return dpnSync
// }

// func TestNewDPNSync(t *testing.T) {
//	if runSyncTests(t) == false {
//		return  // local test cluster isn't running
//	}
//	dpnSync := newDPNSync(t)
//	if dpnSync == nil {
//		return
//	}
// }

// func TestLocalNodeName(t *testing.T) {
//	if runSyncTests(t) == false {
//		return  // local test cluster isn't running
//	}
//	config := loadConfig(t)
//	dpnSync := newDPNSync(t)
//	assert.Equal(t, config.DPN.LocalNode, dpnSync.LocalNodeName())
// }

// func TestRemoteNodeNames(t *testing.T) {
//	if runSyncTests(t) == false {
//		return  // local test cluster isn't running
//	}
//	config := loadConfig(t)
//	dpnSync := newDPNSync(t)
//	remoteNodeNames := dpnSync.RemoteNodeNames()
//	for name, _ := range config.DPN.RemoteNodeURLs {
//		nameIsPresent := false
//		for _, remoteName := range remoteNodeNames {
//			if name == remoteName {
//				nameIsPresent = true
//				break
//			}
//		}
//		assert.True(t, nameIsPresent, name)
//	}
// }

// func TestGetAllNodes(t *testing.T) {
//	if runSyncTests(t) == false {
//		return  // local test cluster isn't running
//	}
//	dpnSync := newDPNSync(t)
//	nodes, err := dpnSync.GetAllNodes()
//	if err != nil {
//		t.Error(err)
//	}
//	assert.Equal(t, 5, len(nodes))
// }

// func TestSyncBags(t *testing.T) {
//	if runSyncTests(t) == false {
//		return  // local test cluster isn't running
//	}
//	dpnSync := newDPNSync(t)
//	nodes, err := dpnSync.GetAllNodes()
//	require.Nil(t, err)
//	for _, node := range nodes {
//		if node.Namespace == "aptrust" {
//			continue
//		}
//		aLongTimeAgo := time.Date(1999, time.December, 31, 23, 0, 0, 0, time.UTC)
//		node.LastPullDate = aLongTimeAgo
//		bagsSynched, err := dpnSync.SyncBags(node)
//		require.Nil(t, err, "Error synching bags for node %s: %v", node.Namespace, err)
//		expectedBagCount := BAG_COUNT
//		assert.Equal(t, expectedBagCount, len(bagsSynched), node.Namespace)
//		for _, remoteBag := range(bagsSynched) {
//			require.NotNil(t, remoteBag, node.Namespace)
//			resp := dpnSync.LocalClient.DPNBagGet(remoteBag.UUID)
//			require.Nil(t, resp.Error)
//			require.NotNil(t, resp.Bag())
//			assert.Equal(t, remoteBag.UpdatedAt, resp.Bag().UpdatedAt)
//		}
//	}
// }

// func TestSyncReplicationRequests(t *testing.T) {
//	if runSyncTests(t) == false {
//		return  // local test cluster isn't running
//	}
//	dpnSync := newDPNSync(t)
//	nodes, err := dpnSync.GetAllNodes()
//	require.Nil(t, err)
//	for _, node := range nodes {
//		if node.Namespace == "aptrust" {
//			continue
//		}
//		aLongTimeAgo := time.Date(1999, time.December, 31, 23, 0, 0, 0, time.UTC)
//		node.LastPullDate = aLongTimeAgo

//		xfersSynched, err := dpnSync.SyncReplicationRequests(node)
//		assert.Nil(t, err, node.Namespace)
//		assert.Equal(t, REPL_COUNT, len(xfersSynched), node.Namespace)

//		for _, xfer := range(xfersSynched) {
//			require.NotNil(t, xfer)
//			resp := dpnSync.LocalClient.ReplicationTransferGet(xfer.ReplicationId)
//			require.Nil(t, resp.Error)
//			require.NotNil(t, resp.ReplicationTransfer())
//			assert.Equal(t, xfer.UpdatedAt, resp.ReplicationTransfer().UpdatedAt, xfer.ReplicationId)
//		}
//	}
// }

// func TestSyncRestoreRequests(t *testing.T) {
//	if runSyncTests(t) == false {
//		return  // local test cluster isn't running
//	}
//	dpnSync := newDPNSync(t)
//	nodes, err := dpnSync.GetAllNodes()
//	require.Nil(t, err)
//	for _, node := range nodes {
//		if node.Namespace == "aptrust" {
//			continue
//		}
//		aLongTimeAgo := time.Date(1999, time.December, 31, 23, 0, 0, 0, time.UTC)
//		node.LastPullDate = aLongTimeAgo
//		xfersSynched, err := dpnSync.SyncRestoreRequests(node)
//		require.Nil(t, err, node.Namespace)
//		assert.Equal(t, RESTORE_COUNT, len(xfersSynched), node.Namespace)
//		for _, xfer := range(xfersSynched) {
//			require.NotNil(t, xfer)
//			resp := dpnSync.LocalClient.RestoreTransferGet(xfer.RestoreId)
//			require.Nil(t, resp.Error)
//			require.NotNil(t, resp.RestoreTransfer())
//			assert.Equal(t, xfer.UpdatedAt, resp.RestoreTransfer().UpdatedAt, node.Namespace)
//		}
//	}
// }

// func TestSyncEverythingFromNode(t *testing.T) {
//	if runSyncTests(t) == false {
//		return  // local test cluster isn't running
//	}
//	dpnSync := newDPNSync(t)

//	// Make 10 bags.
//	// This will also create 40 replication transfers
//	// and 40 restore transfers: one for each bag to
//	// each remote node.
//	bagCount := 10
//	xferCount := 64     // 24 from fixtures in DPN REST + 40 that we just created
//	restoreCount := 44  //  4 from fixtures in DPN REST + 40 that we just created
//	mock := NewMock(dpnSync)
//	err := mock.AddRecordsToNodes(dpnSync.RemoteNodeNames(), bagCount)
//	require.Nil(t, err)
//	nodes, err := dpnSync.GetAllNodes()
//	require.Nil(t, err)

//	for _, node := range nodes {
//		if node.Namespace == "aptrust" {
//			continue
//		}
//		aLongTimeAgo := time.Date(1999, time.December, 31, 23, 0, 0, 0, time.UTC)
//		node.LastPullDate = aLongTimeAgo
//		syncResult := dpnSync.SyncEverythingFromNode(node)

//		// Bags
//		assert.Nil(t, syncResult.BagSyncError)
//		assert.Equal(t, bagCount, len(syncResult.Bags), node.Namespace)

//		// Replication Transfers
//		assert.Nil(t, syncResult.ReplicationSyncError, node.Namespace)
//		assert.Equal(t, xferCount, len(syncResult.ReplicationTransfers), node.Namespace)

//		// Bags
//		assert.Nil(t, syncResult.RestoreSyncError, node.Namespace)
//		assert.Equal(t, restoreCount, len(syncResult.RestoreTransfers), node.Namespace)

//		// Timestamp update
//		resp := dpnSync.LocalClient.NodeGet(node.Namespace)
//		assert.Nil(t, resp.Error, node.Namespace)
//		assert.NotEqual(t, aLongTimeAgo, resp.Node.LastPullDate, node.Namespace)
//	}
// }

func TestHasSyncErrors(t *testing.T) {
	syncResult := &dpn.SyncResult{}
	assert.False(t, syncResult.HasSyncErrors())
	syncResult.BagSyncError = fmt.Errorf("Oops.")
	assert.True(t, syncResult.HasSyncErrors())

	syncResult.BagSyncError = nil
	syncResult.ReplicationSyncError = fmt.Errorf("Oops.")
	assert.True(t, syncResult.HasSyncErrors())

	syncResult.ReplicationSyncError = nil
	syncResult.RestoreSyncError = fmt.Errorf("Oops.")
	assert.True(t, syncResult.HasSyncErrors())
}
<commit_msg>Commented out dpn_sync_tests. Will restore when code is stable.<commit_after>package dpn_test

// import (
//	"fmt"
//	"github.com/APTrust/exchange/context"
//	"github.com/APTrust/exchange/dpn"
//	"github.com/APTrust/exchange/models"
//	"github.com/stretchr/testify/assert"
//	"github.com/stretchr/testify/require"
//	"net/http"
//	"path/filepath"
//	"testing"
//	"time"
// )

// var skipSyncMessagePrinted = false

// const (
//	BAG_COUNT     = 1
//	REPL_COUNT    = 4
//	RESTORE_COUNT = 4
// )

// func loadConfig(t *testing.T) (*models.Config) {
//	configFile := filepath.Join("config", "test.json")
//	config, err := models.LoadConfigFile(configFile)
//	require.Nil(t, err)
//	return config
// }

// func runSyncTests(t *testing.T) bool {
//	config := loadConfig(t)
//	_, err := http.Get(config.DPN.RestClient.LocalServiceURL)
//	if !canRunSyncTests("aptrust", config.DPN.RestClient.LocalServiceURL, err) {
//		return false
//	}
//	for nodeNamespace, url := range config.DPN.RemoteNodeURLs {
//		if url == "" {
//			continue
//		}
//		_, err := http.Get(url)
//		if !canRunSyncTests(nodeNamespace, url, err) {
//			return false
//		}
//	}
//	return true
// }

// func canRunSyncTests(nodeNamespace string, url string, err error) (bool) {
//	if err != nil {
//		if skipSyncMessagePrinted == false {
//			skipSyncMessagePrinted = true
//			fmt.Printf("**** Skipping DPN sync integration tests: "+
//				"%s server is not running at %s\n", nodeNamespace, url)
//			fmt.Println("     Run the run_cluster.sh script in " +
//				"DPN-REST/dpnode to get a local cluster running.")
//		}
//		return false
//	}
//	return true
// }

// func newDPNSync(t *testing.T) (*dpn.DPNSync) {
//	config := loadConfig(t)
//	_context := context.NewContext(config)
//	dpnSync, err := dpn.NewDPNSync(_context)
//	require.Nil(t, err)
//	for namespace, _ := range config.DPN.RemoteNodeTokens {
//		require.NotNil(t, dpnSync.RemoteClients[namespace], namespace)
//	}
//	return dpnSync
// }

// func TestNewDPNSync(t *testing.T) {
//	if runSyncTests(t) == false {
//		return  // local test cluster isn't running
//	}
//	dpnSync := newDPNSync(t)
//	if dpnSync == nil {
//		return
//	}
// }

// func TestLocalNodeName(t *testing.T) {
//	if runSyncTests(t) == false {
//		return  // local test cluster isn't running
//	}
//	config := loadConfig(t)
//	dpnSync := newDPNSync(t)
//	assert.Equal(t, config.DPN.LocalNode, dpnSync.LocalNodeName())
// }

// func TestRemoteNodeNames(t *testing.T) {
//	if runSyncTests(t) == false {
//		return  // local test cluster isn't running
//	}
//	config := loadConfig(t)
//	dpnSync := newDPNSync(t)
//	remoteNodeNames := dpnSync.RemoteNodeNames()
//	for name, _ := range config.DPN.RemoteNodeURLs {
//		nameIsPresent := false
//		for _, remoteName := range remoteNodeNames {
//			if name == remoteName {
//				nameIsPresent = true
//				break
//			}
//		}
//		assert.True(t, nameIsPresent, name)
//	}
// }

// func TestGetAllNodes(t *testing.T) {
//	if runSyncTests(t) == false {
//		return  // local test cluster isn't running
//	}
//	dpnSync := newDPNSync(t)
//	nodes, err := dpnSync.GetAllNodes()
//	if err != nil {
//		t.Error(err)
//	}
//	assert.Equal(t, 5, len(nodes))
// }

// func TestSyncBags(t *testing.T) {
//	if runSyncTests(t) == false {
//		return  // local test cluster isn't running
//	}
//	dpnSync := newDPNSync(t)
//	nodes, err := dpnSync.GetAllNodes()
//	require.Nil(t, err)
//	for _, node := range nodes {
//		if node.Namespace == "aptrust" {
//			continue
//		}
//		aLongTimeAgo := time.Date(1999, time.December, 31, 23, 0, 0, 0, time.UTC)
//		node.LastPullDate = aLongTimeAgo
//		bagsSynched, err := dpnSync.SyncBags(node)
//		require.Nil(t, err, "Error synching bags for node %s: %v", node.Namespace, err)
//		expectedBagCount := BAG_COUNT
//		assert.Equal(t, expectedBagCount, len(bagsSynched), node.Namespace)
//		for _, remoteBag := range(bagsSynched) {
//			require.NotNil(t, remoteBag, node.Namespace)
//			resp := dpnSync.LocalClient.DPNBagGet(remoteBag.UUID)
//			require.Nil(t, resp.Error)
//			require.NotNil(t, resp.Bag())
//			assert.Equal(t, remoteBag.UpdatedAt, resp.Bag().UpdatedAt)
//		}
//	}
// }

// func TestSyncReplicationRequests(t *testing.T) {
//	if runSyncTests(t) == false {
//		return  // local test cluster isn't running
//	}
//	dpnSync := newDPNSync(t)
//	nodes, err := dpnSync.GetAllNodes()
//	require.Nil(t, err)
//	for _, node := range nodes {
//		if node.Namespace == "aptrust" {
//			continue
//		}
//		aLongTimeAgo := time.Date(1999, time.December, 31, 23, 0, 0, 0, time.UTC)
//		node.LastPullDate = aLongTimeAgo

//		xfersSynched, err := dpnSync.SyncReplicationRequests(node)
//		assert.Nil(t, err, node.Namespace)
//		assert.Equal(t, REPL_COUNT, len(xfersSynched), node.Namespace)

//		for _, xfer := range(xfersSynched) {
//			require.NotNil(t, xfer)
//			resp := dpnSync.LocalClient.ReplicationTransferGet(xfer.ReplicationId)
//			require.Nil(t, resp.Error)
//			require.NotNil(t, resp.ReplicationTransfer())
//			assert.Equal(t, xfer.UpdatedAt, resp.ReplicationTransfer().UpdatedAt, xfer.ReplicationId)
//		}
//	}
// }

// func TestSyncRestoreRequests(t *testing.T) {
//	if runSyncTests(t) == false {
//		return  // local test cluster isn't running
//	}
//	dpnSync := newDPNSync(t)
//	nodes, err := dpnSync.GetAllNodes()
//	require.Nil(t, err)
//	for _, node := range nodes {
//		if node.Namespace == "aptrust" {
//			continue
//		}
//		aLongTimeAgo := time.Date(1999, time.December, 31, 23, 0, 0, 0, time.UTC)
//		node.LastPullDate = aLongTimeAgo
//		xfersSynched, err := dpnSync.SyncRestoreRequests(node)
//		require.Nil(t, err, node.Namespace)
//		assert.Equal(t, RESTORE_COUNT, len(xfersSynched), node.Namespace)
//		for _, xfer := range(xfersSynched) {
//			require.NotNil(t, xfer)
//			resp := dpnSync.LocalClient.RestoreTransferGet(xfer.RestoreId)
//			require.Nil(t, resp.Error)
//			require.NotNil(t, resp.RestoreTransfer())
//			assert.Equal(t, xfer.UpdatedAt, resp.RestoreTransfer().UpdatedAt, node.Namespace)
//		}
//	}
// }

// func TestSyncEverythingFromNode(t *testing.T) {
//	if runSyncTests(t) == false {
//		return  // local test cluster isn't running
//	}
//	dpnSync := newDPNSync(t)

//	// Make 10 bags.
//	// This will also create 40 replication transfers
//	// and 40 restore transfers: one for each bag to
//	// each remote node.
//	bagCount := 10
//	xferCount := 64     // 24 from fixtures in DPN REST + 40 that we just created
//	restoreCount := 44  //  4 from fixtures in DPN REST + 40 that we just created
//	mock := NewMock(dpnSync)
//	err := mock.AddRecordsToNodes(dpnSync.RemoteNodeNames(), bagCount)
//	require.Nil(t, err)
//	nodes, err := dpnSync.GetAllNodes()
//	require.Nil(t, err)

//	for _, node := range nodes {
//		if node.Namespace == "aptrust" {
//			continue
//		}
//		aLongTimeAgo := time.Date(1999, time.December, 31, 23, 0, 0, 0, time.UTC)
//		node.LastPullDate = aLongTimeAgo
//		syncResult := dpnSync.SyncEverythingFromNode(node)

//		// Bags
//		assert.Nil(t, syncResult.BagSyncError)
//		assert.Equal(t, bagCount, len(syncResult.Bags), node.Namespace)

//		// Replication Transfers
//		assert.Nil(t, syncResult.ReplicationSyncError, node.Namespace)
//		assert.Equal(t, xferCount, len(syncResult.ReplicationTransfers), node.Namespace)

//		// Bags
//		assert.Nil(t, syncResult.RestoreSyncError, node.Namespace)
//		assert.Equal(t, restoreCount, len(syncResult.RestoreTransfers), node.Namespace)

//		// Timestamp update
//		resp := dpnSync.LocalClient.NodeGet(node.Namespace)
//		assert.Nil(t, resp.Error, node.Namespace)
//		assert.NotEqual(t, aLongTimeAgo, resp.Node.LastPullDate, node.Namespace)
//	}
// }

// func TestHasSyncErrors(t *testing.T) {
//	syncResult := &dpn.SyncResult{}
//	assert.False(t, syncResult.HasSyncErrors())
//	syncResult.BagSyncError = fmt.Errorf("Oops.")
//	assert.True(t, syncResult.HasSyncErrors())

//	syncResult.BagSyncError = nil
//	syncResult.ReplicationSyncError = fmt.Errorf("Oops.")
//	assert.True(t, syncResult.HasSyncErrors())

//	syncResult.ReplicationSyncError = nil
//	syncResult.RestoreSyncError = fmt.Errorf("Oops.")
//	assert.True(t, syncResult.HasSyncErrors())
// }
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"bytes"
	"flag"
	"fmt"
	"os"
	"strconv"
)

var problemNumber = flag.Int(
	"problem", 18, "A problem number from projecteuler.net")
var dataFile = flag.String(
	"datafile", "", "Data file used to solve the problem")

// Sample run:
//
// <built-program> --problem=18 --datafile=<path-to-repo>/data/problem18

func solve18() {
	fd, err := os.Open(*dataFile)
	if err != nil {
		panic(fmt.Sprintf("Failed to open %s: %v", *dataFile, err))
	}
	defer fd.Close()

	reader := bufio.NewReader(fd)
	spaceSep := []byte(" ")
	fmt.Printf("Reading data from file ... %s\n", *dataFile)

	var numbers [][]int

	line, _, err := reader.ReadLine()
	for err == nil && len(line) > 0 && line[0] != '#' {
		// Add a new row of numbers
		var rowNumbers []int
		lineData := bytes.Split(line, spaceSep)
		for _, element := range lineData {
			number, _ := strconv.Atoi(string(element))
			rowNumbers = append(rowNumbers, number)
		}
		// Add the row to our collection and move to the next line
		numbers = append(numbers, rowNumbers)
		line, _, err = reader.ReadLine()
	}

	// Make sure we have some minimum amount of data
	if len(numbers) < 2 {
		panic(fmt.Sprintf("Need atleast two rows of data !"))
	}

	// File is read, now start building up partial sums
	for rowNum := len(numbers) - 2; rowNum >= 0; rowNum-- {
		for index, number := range numbers[rowNum] {
			// Update the number to be a partial sum
			sum1 := number + numbers[rowNum+1][index]
			// TODO(agam): Check that each row has the right
			// number of elements.
			sum2 := number + numbers[rowNum+1][index+1]
			if sum1 > sum2 {
				numbers[rowNum][index] = sum1
			} else {
				numbers[rowNum][index] = sum2
			}
		}
	}

	// At the end of this process, the answer should be at the top
	fmt.Printf("The answer is [%d]\n", numbers[0][0])
}

func main() {
	flag.Parse()
	fmt.Printf("Solving problem %d\n", *problemNumber)
	switch *problemNumber {
	case 18:
		solve18()
	}
}
<commit_msg>solved problem 19<commit_after>package main

import (
	"bufio"
	"bytes"
	"flag"
	"fmt"
	"os"
	"strconv"
	"time"
)

var problemNumber = flag.Int(
	"problem", 18, "A problem number from projecteuler.net")
var dataFile = flag.String(
	"datafile", "", "Data file used to solve the problem")

// Sample run:
//
// <built-program> --problem=18 --datafile=<path-to-repo>/data/problem18

func solve18() {
	fd, err := os.Open(*dataFile)
	if err != nil {
		panic(fmt.Sprintf("Failed to open %s: %v", *dataFile, err))
	}
	defer fd.Close()

	reader := bufio.NewReader(fd)
	spaceSep := []byte(" ")
	fmt.Printf("Reading data from file ... %s\n", *dataFile)

	var numbers [][]int

	line, _, err := reader.ReadLine()
	for err == nil && len(line) > 0 && line[0] != '#' {
		// Add a new row of numbers
		var rowNumbers []int
		lineData := bytes.Split(line, spaceSep)
		for _, element := range lineData {
			number, _ := strconv.Atoi(string(element))
			rowNumbers = append(rowNumbers, number)
		}
		// Add the row to our collection and move to the next line
		numbers = append(numbers, rowNumbers)
		line, _, err = reader.ReadLine()
	}

	// Make sure we have some minimum amount of data
	if len(numbers) < 2 {
		panic(fmt.Sprintf("Need atleast two rows of data !"))
	}

	// File is read, now start building up partial sums
	for rowNum := len(numbers) - 2; rowNum >= 0; rowNum-- {
		for index, number := range numbers[rowNum] {
			// Update the number to be a partial sum
			sum1 := number + numbers[rowNum+1][index]
			// TODO(agam): Check that each row has the right
			// number of elements.
			sum2 := number + numbers[rowNum+1][index+1]
			if sum1 > sum2 {
				numbers[rowNum][index] = sum1
			} else {
				numbers[rowNum][index] = sum2
			}
		}
	}

	// At the end of this process, the answer should be at the top
	fmt.Printf("The answer is [%d]\n", numbers[0][0])
}

func solve19() {
	// Go through all the first of the months and count the sundays
	sundayCount := 0
	month := time.January
	year := 1901
	for year < 2001 {
		day := time.Date(year, month, 1, 0, 0, 0, 0, time.UTC)
		if day.Weekday() == time.Sunday {
			sundayCount++
		}
		if month == time.December {
			month = time.January
			year++
		} else {
			month++
		}
	}
	fmt.Printf("The answer is [%d]\n", sundayCount)
}

func main() {
	flag.Parse()
	fmt.Printf("Solving problem %d\n", *problemNumber)
	switch *problemNumber {
	case 18:
		solve18()
		return
	case 19:
		solve19()
		return
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package etcdmain

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"path"
	"reflect"
	"strings"
	"time"

	"github.com/coreos/etcd/discovery"
	"github.com/coreos/etcd/etcdserver"
	"github.com/coreos/etcd/etcdserver/etcdhttp"
	"github.com/coreos/etcd/pkg/cors"
	"github.com/coreos/etcd/pkg/fileutil"
	"github.com/coreos/etcd/pkg/osutil"
	"github.com/coreos/etcd/pkg/transport"
	"github.com/coreos/etcd/pkg/types"
	"github.com/coreos/etcd/proxy"
	"github.com/coreos/etcd/rafthttp"

	"github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/pkg/capnslog"
)

type dirType string

var log = capnslog.NewPackageLogger("github.com/coreos/etcd", "etcdmain")

const (
	// the owner can make/remove files inside the directory
	privateDirMode = 0700
)

var (
	dirMember = dirType("member")
	dirProxy  = dirType("proxy")
	dirEmpty  = dirType("empty")
)

func Main() {
	capnslog.SetFormatter(capnslog.NewStringFormatter(os.Stderr))
	cfg := NewConfig()
	err := cfg.Parse(os.Args[1:])
	if err != nil {
		log.Printf("error verifying flags, %v. See 'etcd -help'.", err)
		switch err {
		case errUnsetAdvertiseClientURLsFlag:
			log.Printf("When listening on specific address(es), this etcd process must advertise accessible url(s) to each connected client.")
		}
		os.Exit(1)
	}
	setupLogging(cfg)

	var stopped <-chan struct{}

	if cfg.name != defaultName && cfg.initialCluster == initialClusterFromName(defaultName) {
		cfg.initialCluster = initialClusterFromName(cfg.name)
	}

	if cfg.dir == "" {
		cfg.dir = fmt.Sprintf("%v.etcd", cfg.name)
		log.Printf("no data-dir provided, using default data-dir ./%s", cfg.dir)
	}

	which := identifyDataDirOrDie(cfg.dir)
	if which != dirEmpty {
		log.Printf("already initialized as %v before, starting as etcd %v...", which, which)
	}

	shouldProxy := cfg.isProxy() || which == dirProxy
	if !shouldProxy {
		stopped, err = startEtcd(cfg)
		if err == discovery.ErrFullCluster && cfg.shouldFallbackToProxy() {
			log.Printf("discovery cluster full, falling back to %s", fallbackFlagProxy)
			shouldProxy = true
		}
	}
	if shouldProxy {
		err = startProxy(cfg)
	}
	if err != nil {
		switch err {
		case discovery.ErrDuplicateID:
			log.Fatalf("member %s has previously registered with discovery service (%s), but the data-dir (%s) on disk cannot be found.",
				cfg.name, cfg.durl, cfg.dir)
		default:
			log.Fatalf("%v", err)
		}
	}

	osutil.HandleInterrupts()

	<-stopped
	osutil.Exit(0)
}

// startEtcd launches the etcd server and HTTP handlers for client/server communication.
func startEtcd(cfg *config) (<-chan struct{}, error) {
	cls, err := setupCluster(cfg)
	if err != nil {
		return nil, fmt.Errorf("error setting up initial cluster: %v", err)
	}

	pt, err := transport.NewTimeoutTransport(cfg.peerTLSInfo, rafthttp.DialTimeout, rafthttp.ConnReadTimeout, rafthttp.ConnWriteTimeout)
	if err != nil {
		return nil, err
	}

	if !cfg.peerTLSInfo.Empty() {
		log.Printf("peerTLS: %s", cfg.peerTLSInfo)
	}
	plns := make([]net.Listener, 0)
	for _, u := range cfg.lpurls {
		var l net.Listener
		l, err = transport.NewTimeoutListener(u.Host, u.Scheme, cfg.peerTLSInfo, rafthttp.ConnReadTimeout, rafthttp.ConnWriteTimeout)
		if err != nil {
			return nil, err
		}

		urlStr := u.String()
		log.Print("listening for peers on ", urlStr)
		defer func() {
			if err != nil {
				l.Close()
				log.Print("stopping listening for peers on ", urlStr)
			}
		}()
		plns = append(plns, l)
	}

	if !cfg.clientTLSInfo.Empty() {
		log.Printf("clientTLS: %s", cfg.clientTLSInfo)
	}
	clns := make([]net.Listener, 0)
	for _, u := range cfg.lcurls {
		var l net.Listener
		l, err = transport.NewKeepAliveListener(u.Host, u.Scheme, cfg.clientTLSInfo)
		if err != nil {
			return nil, err
		}

		urlStr := u.String()
		log.Print("listening for client requests on ", urlStr)
		defer func() {
			if err != nil {
				l.Close()
				log.Print("stopping listening for client requests on ", urlStr)
			}
		}()
		clns = append(clns, l)
	}

	srvcfg := &etcdserver.ServerConfig{
		Name:            cfg.name,
		ClientURLs:      cfg.acurls,
		PeerURLs:        cfg.apurls,
		DataDir:         cfg.dir,
		SnapCount:       cfg.snapCount,
		MaxSnapFiles:    cfg.maxSnapFiles,
		MaxWALFiles:     cfg.maxWalFiles,
		Cluster:         cls,
		DiscoveryURL:    cfg.durl,
		DiscoveryProxy:  cfg.dproxy,
		NewCluster:      cfg.isNewCluster(),
		ForceNewCluster: cfg.forceNewCluster,
		Transport:       pt,
		TickMs:          cfg.TickMs,
		ElectionTicks:   cfg.electionTicks(),
	}
	var s *etcdserver.EtcdServer
	s, err = etcdserver.NewServer(srvcfg)
	if err != nil {
		return nil, err
	}
	s.Start()
	osutil.RegisterInterruptHandler(s.Stop)

	if cfg.corsInfo.String() != "" {
		log.Printf("cors = %s", cfg.corsInfo)
	}
	ch := &cors.CORSHandler{
		Handler: etcdhttp.NewClientHandler(s),
		Info:    cfg.corsInfo,
	}
	ph := etcdhttp.NewPeerHandler(s.Cluster, s.RaftHandler())
	// Start the peer server in a goroutine
	for _, l := range plns {
		go func(l net.Listener) {
			log.Fatal(serveHTTP(l, ph, 5*time.Minute))
		}(l)
	}
	// Start a client server goroutine for each listen address
	for _, l := range clns {
		go func(l net.Listener) {
			// read timeout does not work with http close notify
			// TODO: https://github.com/golang/go/issues/9524
			log.Fatal(serveHTTP(l, ch, 0))
		}(l)
	}
	return s.StopNotify(), nil
}

// startProxy launches an HTTP proxy for client communication which proxies to other etcd nodes.
func startProxy(cfg *config) error {
	cls, err := setupCluster(cfg)
	if err != nil {
		return fmt.Errorf("error setting up initial cluster: %v", err)
	}

	if cfg.durl != "" {
		s, err := discovery.GetCluster(cfg.durl, cfg.dproxy)
		if err != nil {
			return err
		}
		if cls, err = etcdserver.NewClusterFromString(cfg.durl, s); err != nil {
			return err
		}
	}

	pt, err := transport.NewTransport(cfg.clientTLSInfo)
	if err != nil {
		return err
	}

	tr, err := transport.NewTransport(cfg.peerTLSInfo)
	if err != nil {
		return err
	}

	cfg.dir = path.Join(cfg.dir, "proxy")
	err = os.MkdirAll(cfg.dir, 0700)
	if err != nil {
		return err
	}

	var peerURLs []string
	clusterfile := path.Join(cfg.dir, "cluster")

	b, err := ioutil.ReadFile(clusterfile)
	switch {
	case err == nil:
		urls := struct{ PeerURLs []string }{}
		err := json.Unmarshal(b, &urls)
		if err != nil {
			return err
		}
		peerURLs = urls.PeerURLs
		log.Printf("proxy: using peer urls %v from cluster file ./%s", peerURLs, clusterfile)
	case os.IsNotExist(err):
		peerURLs = cls.PeerURLs()
		log.Printf("proxy: using peer urls %v ", peerURLs)
	default:
		return err
	}

	uf := func() []string {
		gcls, err := etcdserver.GetClusterFromRemotePeers(peerURLs, tr)
		// TODO: remove the 2nd check when we fix GetClusterFromPeers
		// GetClusterFromPeers should not return nil error with an invaild empty cluster
		if err != nil {
			log.Printf("proxy: %v", err)
			return []string{}
		}
		if len(gcls.Members()) == 0 {
			return cls.ClientURLs()
		}
		cls = gcls

		urls := struct{ PeerURLs []string }{cls.PeerURLs()}
		b, err := json.Marshal(urls)
		if err != nil {
			log.Printf("proxy: error on marshal peer urls %s", err)
			return cls.ClientURLs()
		}

		err = ioutil.WriteFile(clusterfile+".bak", b, 0600)
		if err != nil {
			log.Printf("proxy: error on writing urls %s", err)
			return cls.ClientURLs()
		}
		err = os.Rename(clusterfile+".bak", clusterfile)
		if err != nil {
			log.Printf("proxy: error on updating clusterfile %s", err)
			return cls.ClientURLs()
		}
		if !reflect.DeepEqual(cls.PeerURLs(), peerURLs) {
			log.Printf("proxy: updated peer urls in cluster file from %v to %v", peerURLs, cls.PeerURLs())
		}
		peerURLs = cls.PeerURLs()

		return cls.ClientURLs()
	}
	ph := proxy.NewHandler(pt, uf)
	ph = &cors.CORSHandler{
		Handler: ph,
		Info:    cfg.corsInfo,
	}

	if cfg.isReadonlyProxy() {
		ph = proxy.NewReadonlyHandler(ph)
	}
	// Start a proxy server goroutine for each listen address
	for _, u := range cfg.lcurls {
		l, err := transport.NewListener(u.Host, u.Scheme, cfg.clientTLSInfo)
		if err != nil {
			return err
		}

		host := u.Host
		go func() {
			log.Print("proxy: listening for client requests on ", host)
			log.Fatal(http.Serve(l, ph))
		}()
	}
	return nil
}

// setupCluster sets up an initial cluster definition for bootstrap or discovery.
func setupCluster(cfg *config) (*etcdserver.Cluster, error) {
	var cls *etcdserver.Cluster
	var err error
	switch {
	case cfg.durl != "":
		// If using discovery, generate a temporary cluster based on
		// self's advertised peer URLs
		clusterStr := genClusterString(cfg.name, cfg.apurls)
		cls, err = etcdserver.NewClusterFromString(cfg.durl, clusterStr)
	case cfg.dnsCluster != "":
		clusterStr, clusterToken, err := discovery.SRVGetCluster(cfg.name, cfg.dnsCluster, cfg.initialClusterToken, cfg.apurls)
		if err != nil {
			return nil, err
		}
		cls, err = etcdserver.NewClusterFromString(clusterToken, clusterStr)
	default:
		// We're statically configured, and cluster has appropriately been set.
		cls, err = etcdserver.NewClusterFromString(cfg.initialClusterToken, cfg.initialCluster)
	}
	return cls, err
}

func genClusterString(name string, urls types.URLs) string {
	addrs := make([]string, 0)
	for _, u := range urls {
		addrs = append(addrs, fmt.Sprintf("%v=%v", name, u.String()))
	}
	return strings.Join(addrs, ",")
}

// identifyDataDirOrDie returns the type of the data dir.
// Dies if the datadir is invalid.
func identifyDataDirOrDie(dir string) dirType {
	names, err := fileutil.ReadDir(dir)
	if err != nil {
		if os.IsNotExist(err) {
			return dirEmpty
		}
		log.Fatalf("error listing data dir: %s", dir)
	}

	var m, p bool
	for _, name := range names {
		switch dirType(name) {
		case dirMember:
			m = true
		case dirProxy:
			p = true
		default:
			log.Printf("found invalid file/dir %s under data dir %s (Ignore this if you are upgrading etcd)", name, dir)
		}
	}

	if m && p {
		log.Fatal("invalid datadir. Both member and proxy directories exist.")
	}
	if m {
		return dirMember
	}
	if p {
		return dirProxy
	}
	return dirEmpty
}

func setupLogging(cfg *config) {
	capnslog.SetGlobalLogLevel(capnslog.INFO)
	if cfg.debug {
		capnslog.SetGlobalLogLevel(capnslog.DEBUG)
	}
	if cfg.logPkgLevels != "" {
		repoLog := capnslog.MustRepoLogger("github.com/coreos/etcd")
		settings, err := repoLog.ParseLogLevelConfig(cfg.logPkgLevels)
		if err != nil {
			log.Warningf("Couldn't parse log level string: %s, continuing with default levels", err.Error())
			return
		}
		repoLog.SetLogLevel(settings)
	}
}
<commit_msg>etcdmain: better error msg when detected duplicate id in discovery<commit_after>// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package etcdmain

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"path"
	"reflect"
	"strings"
	"time"

	"github.com/coreos/etcd/discovery"
	"github.com/coreos/etcd/etcdserver"
	"github.com/coreos/etcd/etcdserver/etcdhttp"
	"github.com/coreos/etcd/pkg/cors"
	"github.com/coreos/etcd/pkg/fileutil"
	"github.com/coreos/etcd/pkg/osutil"
	"github.com/coreos/etcd/pkg/transport"
	"github.com/coreos/etcd/pkg/types"
	"github.com/coreos/etcd/proxy"
	"github.com/coreos/etcd/rafthttp"

	"github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/pkg/capnslog"
)

type dirType string

var log = capnslog.NewPackageLogger("github.com/coreos/etcd", "etcdmain")

const (
	// the owner can make/remove files inside the directory
	privateDirMode = 0700
)

var (
	dirMember = dirType("member")
	dirProxy  = dirType("proxy")
	dirEmpty  = dirType("empty")
)

func Main() {
	capnslog.SetFormatter(capnslog.NewStringFormatter(os.Stderr))
	cfg := NewConfig()
	err := cfg.Parse(os.Args[1:])
	if err != nil {
		log.Printf("error verifying flags, %v. See 'etcd -help'.", err)
		switch err {
		case errUnsetAdvertiseClientURLsFlag:
			log.Printf("When listening on specific address(es), this etcd process must advertise accessible url(s) to each connected client.")
		}
		os.Exit(1)
	}
	setupLogging(cfg)

	var stopped <-chan struct{}

	if cfg.name != defaultName && cfg.initialCluster == initialClusterFromName(defaultName) {
		cfg.initialCluster = initialClusterFromName(cfg.name)
	}

	if cfg.dir == "" {
		cfg.dir = fmt.Sprintf("%v.etcd", cfg.name)
		log.Printf("no data-dir provided, using default data-dir ./%s", cfg.dir)
	}

	which := identifyDataDirOrDie(cfg.dir)
	if which != dirEmpty {
		log.Printf("already initialized as %v before, starting as etcd %v...", which, which)
	}

	shouldProxy := cfg.isProxy() || which == dirProxy
	if !shouldProxy {
		stopped, err = startEtcd(cfg)
		if err == discovery.ErrFullCluster && cfg.shouldFallbackToProxy() {
			log.Printf("discovery cluster full, falling back to %s", fallbackFlagProxy)
			shouldProxy = true
		}
	}
	if shouldProxy {
		err = startProxy(cfg)
	}
	if err != nil {
		switch err {
		case discovery.ErrDuplicateID:
			log.Printf("member %q has previously registered with discovery service token (%s).", cfg.name, cfg.durl)
			log.Printf("But etcd could not find vaild cluster configuration in the given data dir (%s).", cfg.dir)
			log.Printf("Please check the given data dir path if the previous bootstrap succeeded")
			log.Printf("or use a new discovery token if the previous bootstrap failed.")
		default:
			log.Fatalf("%v", err)
		}
	}

	osutil.HandleInterrupts()

	<-stopped
	osutil.Exit(0)
}

// startEtcd launches the etcd server and HTTP handlers for client/server communication.
func startEtcd(cfg *config) (<-chan struct{}, error) {
	cls, err := setupCluster(cfg)
	if err != nil {
		return nil, fmt.Errorf("error setting up initial cluster: %v", err)
	}

	pt, err := transport.NewTimeoutTransport(cfg.peerTLSInfo, rafthttp.DialTimeout, rafthttp.ConnReadTimeout, rafthttp.ConnWriteTimeout)
	if err != nil {
		return nil, err
	}

	if !cfg.peerTLSInfo.Empty() {
		log.Printf("peerTLS: %s", cfg.peerTLSInfo)
	}
	plns := make([]net.Listener, 0)
	for _, u := range cfg.lpurls {
		var l net.Listener
		l, err = transport.NewTimeoutListener(u.Host, u.Scheme, cfg.peerTLSInfo, rafthttp.ConnReadTimeout, rafthttp.ConnWriteTimeout)
		if err != nil {
			return nil, err
		}

		urlStr := u.String()
		log.Print("listening for peers on ", urlStr)
		defer func() {
			if err != nil {
				l.Close()
				log.Print("stopping listening for peers on ", urlStr)
			}
		}()
		plns = append(plns, l)
	}

	if !cfg.clientTLSInfo.Empty() {
		log.Printf("clientTLS: %s", cfg.clientTLSInfo)
	}
	clns := make([]net.Listener, 0)
	for _, u := range cfg.lcurls {
		var l net.Listener
		l, err = transport.NewKeepAliveListener(u.Host, u.Scheme, cfg.clientTLSInfo)
		if err != nil {
			return nil, err
		}

		urlStr := u.String()
		log.Print("listening for client requests on ", urlStr)
		defer func() {
			if err != nil {
				l.Close()
				log.Print("stopping listening for client requests on ", urlStr)
			}
		}()
		clns = append(clns, l)
	}

	srvcfg := &etcdserver.ServerConfig{
		Name:            cfg.name,
		ClientURLs:      cfg.acurls,
		PeerURLs:        cfg.apurls,
		DataDir:         cfg.dir,
		SnapCount:       cfg.snapCount,
		MaxSnapFiles:    cfg.maxSnapFiles,
		MaxWALFiles:     cfg.maxWalFiles,
		Cluster:         cls,
		DiscoveryURL:    cfg.durl,
		DiscoveryProxy:  cfg.dproxy,
		NewCluster:      cfg.isNewCluster(),
		ForceNewCluster: cfg.forceNewCluster,
		Transport:       pt,
		TickMs:          cfg.TickMs,
		ElectionTicks:   cfg.electionTicks(),
	}
	var s *etcdserver.EtcdServer
	s, err = etcdserver.NewServer(srvcfg)
	if err != nil {
		return nil, err
	}
	s.Start()
	osutil.RegisterInterruptHandler(s.Stop)

	if cfg.corsInfo.String() != "" {
		log.Printf("cors = %s", cfg.corsInfo)
	}
	ch := &cors.CORSHandler{
		Handler: etcdhttp.NewClientHandler(s),
		Info:    cfg.corsInfo,
	}
	ph := etcdhttp.NewPeerHandler(s.Cluster, s.RaftHandler())
	// Start the peer server in a goroutine
	for _, l := range plns {
		go func(l net.Listener) {
			log.Fatal(serveHTTP(l, ph, 5*time.Minute))
		}(l)
	}
	// Start a client server goroutine for each listen address
	for _, l := range clns {
		go func(l net.Listener) {
			// read timeout does not work with http close notify
			// TODO: https://github.com/golang/go/issues/9524
			log.Fatal(serveHTTP(l, ch, 0))
		}(l)
	}
	return s.StopNotify(), nil
}

// startProxy launches an HTTP proxy for client communication which proxies to other etcd nodes.
func startProxy(cfg *config) error {
	cls, err := setupCluster(cfg)
	if err != nil {
		return fmt.Errorf("error setting up initial cluster: %v", err)
	}

	if cfg.durl != "" {
		s, err := discovery.GetCluster(cfg.durl, cfg.dproxy)
		if err != nil {
			return err
		}
		if cls, err = etcdserver.NewClusterFromString(cfg.durl, s); err != nil {
			return err
		}
	}

	pt, err := transport.NewTransport(cfg.clientTLSInfo)
	if err != nil {
		return err
	}

	tr, err := transport.NewTransport(cfg.peerTLSInfo)
	if err != nil {
		return err
	}

	cfg.dir = path.Join(cfg.dir, "proxy")
	err = os.MkdirAll(cfg.dir, 0700)
	if err != nil {
		return err
	}

	var peerURLs []string
	clusterfile := path.Join(cfg.dir, "cluster")

	b, err := ioutil.ReadFile(clusterfile)
	switch {
	case err == nil:
		urls := struct{ PeerURLs []string }{}
		err := json.Unmarshal(b, &urls)
		if err != nil {
			return err
		}
		peerURLs = urls.PeerURLs
		log.Printf("proxy: using peer urls %v from cluster file ./%s", peerURLs, clusterfile)
	case os.IsNotExist(err):
		peerURLs = cls.PeerURLs()
		log.Printf("proxy: using peer urls %v ", peerURLs)
	default:
		return err
	}

	uf := func() []string {
		gcls, err := etcdserver.GetClusterFromRemotePeers(peerURLs, tr)
		// TODO: remove the 2nd check when we fix GetClusterFromPeers
		// GetClusterFromPeers should not return nil error with an invaild empty cluster
		if err != nil {
			log.Printf("proxy: %v", err)
			return []string{}
		}
		if len(gcls.Members()) == 0 {
			return cls.ClientURLs()
		}
		cls = gcls

		urls := struct{ PeerURLs []string }{cls.PeerURLs()}
		b, err := json.Marshal(urls)
		if err != nil {
			log.Printf("proxy: error on marshal peer urls %s", err)
			return cls.ClientURLs()
		}

		err = ioutil.WriteFile(clusterfile+".bak", b, 0600)
		if err != nil {
			log.Printf("proxy: error on writing urls %s", err)
			return cls.ClientURLs()
		}
		err = os.Rename(clusterfile+".bak", clusterfile)
		if err != nil {
			log.Printf("proxy: error on updating clusterfile %s", err)
			return cls.ClientURLs()
		}
		if !reflect.DeepEqual(cls.PeerURLs(), peerURLs) {
			log.Printf("proxy: updated peer urls in cluster file from %v to %v", peerURLs, cls.PeerURLs())
		}
		peerURLs = cls.PeerURLs()

		return cls.ClientURLs()
	}
	ph := proxy.NewHandler(pt, uf)
	ph = &cors.CORSHandler{
		Handler: ph,
		Info:    cfg.corsInfo,
	}

	if cfg.isReadonlyProxy() {
		ph = proxy.NewReadonlyHandler(ph)
	}
	// Start a proxy server goroutine for each listen address
	for _, u := range cfg.lcurls {
		l, err := transport.NewListener(u.Host, u.Scheme, cfg.clientTLSInfo)
		if err != nil {
			return err
		}

		host := u.Host
		go func() {
			log.Print("proxy: listening for client requests on ", host)
			log.Fatal(http.Serve(l, ph))
		}()
	}
	return nil
}

// setupCluster sets up an initial cluster definition for bootstrap or discovery.
func setupCluster(cfg *config) (*etcdserver.Cluster, error) {
	var cls *etcdserver.Cluster
	var err error
	switch {
	case cfg.durl != "":
		// If using discovery, generate a temporary cluster based on
		// self's advertised peer URLs
		clusterStr := genClusterString(cfg.name, cfg.apurls)
		cls, err = etcdserver.NewClusterFromString(cfg.durl, clusterStr)
	case cfg.dnsCluster != "":
		clusterStr, clusterToken, err := discovery.SRVGetCluster(cfg.name, cfg.dnsCluster, cfg.initialClusterToken, cfg.apurls)
		if err != nil {
			return nil, err
		}
		cls, err = etcdserver.NewClusterFromString(clusterToken, clusterStr)
	default:
		// We're statically configured, and cluster has appropriately been set.
		cls, err = etcdserver.NewClusterFromString(cfg.initialClusterToken, cfg.initialCluster)
	}
	return cls, err
}

func genClusterString(name string, urls types.URLs) string {
	addrs := make([]string, 0)
	for _, u := range urls {
		addrs = append(addrs, fmt.Sprintf("%v=%v", name, u.String()))
	}
	return strings.Join(addrs, ",")
}

// identifyDataDirOrDie returns the type of the data dir.
// Dies if the datadir is invalid.
func identifyDataDirOrDie(dir string) dirType {
	names, err := fileutil.ReadDir(dir)
	if err != nil {
		if os.IsNotExist(err) {
			return dirEmpty
		}
		log.Fatalf("error listing data dir: %s", dir)
	}

	var m, p bool
	for _, name := range names {
		switch dirType(name) {
		case dirMember:
			m = true
		case dirProxy:
			p = true
		default:
			log.Printf("found invalid file/dir %s under data dir %s (Ignore this if you are upgrading etcd)", name, dir)
		}
	}

	if m && p {
		log.Fatal("invalid datadir. Both member and proxy directories exist.")
	}
	if m {
		return dirMember
	}
	if p {
		return dirProxy
	}
	return dirEmpty
}

func setupLogging(cfg *config) {
	capnslog.SetGlobalLogLevel(capnslog.INFO)
	if cfg.debug {
		capnslog.SetGlobalLogLevel(capnslog.DEBUG)
	}
	if cfg.logPkgLevels != "" {
		repoLog := capnslog.MustRepoLogger("github.com/coreos/etcd")
		settings, err := repoLog.ParseLogLevelConfig(cfg.logPkgLevels)
		if err != nil {
			log.Warningf("Couldn't parse log level string: %s, continuing with default levels", err.Error())
			return
		}
		repoLog.SetLogLevel(settings)
	}
}
<|endoftext|>


<commit_before>package ethernet

import (
	"bytes"
	"io"
	"net"
	"reflect"
	"testing"
)

func TestFrameMarshalBinary(t *testing.T) {
	var tests = []struct {
		desc string
		f    *Frame
		b    []byte
		err  error
	}{
		{
			desc: "VLAN priority too large",
			f: &Frame{
				VLAN: []*VLAN{{
					Priority: 8,
				}},
			},
			err: ErrInvalidVLAN,
		},
		{
			desc: "VLAN ID too large",
			f: &Frame{
				VLAN: []*VLAN{{
					ID: VLANMax,
				}},
			},
			err: ErrInvalidVLAN,
		},
		{
			desc: "IPv4, no VLANs",
			f: &Frame{
				DestinationHardwareAddr: net.HardwareAddr{0, 1, 0, 1, 0, 1},
				SourceHardwareAddr:      net.HardwareAddr{1, 0, 1, 0, 1, 0},
				EtherType:               EtherTypeIPv4,
				Payload:                 bytes.Repeat([]byte{0}, 50),
			},
			b: append([]byte{
				0, 1, 0, 1, 0, 1,
				1, 0, 1, 0, 1, 0,
				0x08, 0x00,
			}, bytes.Repeat([]byte{0}, 50)...),
		},
		{
			desc: "IPv6, 1 VLAN: PRI 1, ID 101",
			f: &Frame{
				DestinationHardwareAddr: net.HardwareAddr{1, 0, 1, 0, 1, 0},
				SourceHardwareAddr:      net.HardwareAddr{0, 1, 0, 1, 0, 1},
				VLAN: []*VLAN{{
					Priority: 1,
					ID:       101,
				}},
				EtherType: EtherTypeIPv6,
				Payload:   bytes.Repeat([]byte{0}, 50),
			},
			b: append([]byte{
				1, 0, 1, 0, 1, 0,
				0, 1, 0, 1, 0, 1,
				0x81, 0x00,
				0x20, 0x65,
				0x86, 0xDD,
			}, bytes.Repeat([]byte{0}, 50)...),
		},
		{
			desc: "ARP, 2 VLANs: (PRI 0, DROP, ID 100) (PRI 1, ID 101)",
			f: &Frame{
				DestinationHardwareAddr: Broadcast,
				SourceHardwareAddr:      net.HardwareAddr{0, 1, 0, 1, 0, 1},
				VLAN: []*VLAN{
					{
						DropEligible: true,
						ID:           100,
					},
					{
						Priority: 1,
						ID:       101,
					},
				},
				EtherType: EtherTypeARP,
				Payload:   bytes.Repeat([]byte{0}, 50),
			},
			b: append([]byte{
				0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
				0, 1, 0, 1, 0, 1,
				0x81, 0x00,
				0x10, 0x64,
				0x81, 0x00,
				0x20, 0x65,
				0x08, 0x06,
			}, bytes.Repeat([]byte{0}, 50)...),
		},
	}

	for i, tt := range tests {
		b, err := tt.f.MarshalBinary()
		if err != nil {
			if want, got := tt.err, err; want != got {
				t.Fatalf("[%02d] test %q, unexpected error: %v != %v",
					i, tt.desc, want, got)
			}

			continue
		}

		if want, got := tt.b, b; !bytes.Equal(want, got) {
			t.Fatalf("[%02d] test %q, unexpected Frame bytes:\n- want: %v\n-  got: %v",
				i, tt.desc, want, got)
		}
	}
}

func TestFrameUnmarshalBinary(t *testing.T) {
	var tests = []struct {
		desc string
		b    []byte
		f    *Frame
		err  error
	}{
		{
			desc: "nil buffer",
			err:  io.ErrUnexpectedEOF,
		},
		{
			desc: "short buffer",
			b:    bytes.Repeat([]byte{0}, 13),
			err:  io.ErrUnexpectedEOF,
		},
		{
			desc: "short payload, no VLANs",
			b:    bytes.Repeat([]byte{0}, 45),
			err:  io.ErrUnexpectedEOF,
		},
		{
			desc: "1 short VLAN",
			b: []byte{
				0, 0, 0, 0, 0, 0,
				0, 0, 0, 0, 0, 0,
				0x81, 0x00,
				0x00,
			},
			err: io.ErrUnexpectedEOF,
		},
		{
			desc: "VLAN ID too large",
			b: []byte{
				0, 0, 0, 0, 0, 0,
				0, 0, 0, 0, 0, 0,
				0x81, 0x00,
				0xff, 0xff,
			},
			err: ErrInvalidVLAN,
		},
		{
			desc: "short payload, 1 VLAN",
			b: append([]byte{
				0, 0, 0, 0, 0, 0,
				0, 0, 0, 0, 0, 0,
				0x81, 0x00,
				0x00, 0x00,
				0x00, 0x00,
			}, bytes.Repeat([]byte{0}, 41)...),
			err: io.ErrUnexpectedEOF,
		},
		{
			desc: "short payload, 2 VLANs",
			b: append([]byte{
				0, 0, 0, 0, 0, 0,
				0, 0, 0, 0, 0, 0,
				0x81, 0x00,
				0x00, 0x00,
				0x81, 0x00,
				0x00, 0x00,
				0x00, 0x00,
			}, bytes.Repeat([]byte{0}, 37)...),
			err: io.ErrUnexpectedEOF,
		},
		{
			desc: "0 VLANs detected, but 1 may have been present",
			b:    bytes.Repeat([]byte{0}, 56),
			f: &Frame{
				DestinationHardwareAddr: net.HardwareAddr{0, 0, 0, 0, 0, 0},
				SourceHardwareAddr:      net.HardwareAddr{0, 0, 0, 0, 0, 0},
				Payload:                 bytes.Repeat([]byte{0}, 42),
			},
		},
		{
			desc: "IPv4, no VLANs",
			b: append([]byte{
				0, 1, 0, 1, 0, 1,
				1, 0, 1, 0, 1, 0,
				0x08, 0x00,
			}, bytes.Repeat([]byte{0}, 50)...),
			f: &Frame{
				DestinationHardwareAddr: net.HardwareAddr{0, 1, 0, 1, 0, 1},
				SourceHardwareAddr:      net.HardwareAddr{1, 0, 1, 0, 1, 0},
				EtherType:               EtherTypeIPv4,
				Payload:                 bytes.Repeat([]byte{0}, 50),
			},
		},
		{
			desc: "IPv6, 1 VLAN: PRI 1, ID 101",
			b: append([]byte{
				1, 0, 1, 0, 1, 0,
				0, 1, 0, 1, 0, 1,
				0x81, 0x00,
				0x20, 0x65,
				0x86, 0xDD,
			}, bytes.Repeat([]byte{0}, 50)...),
			f: &Frame{
				DestinationHardwareAddr: net.HardwareAddr{1, 0, 1, 0, 1, 0},
				SourceHardwareAddr:      net.HardwareAddr{0, 1, 0, 1, 0, 1},
				VLAN: []*VLAN{{
					Priority: 1,
					ID:       101,
				}},
				EtherType: EtherTypeIPv6,
				Payload:   bytes.Repeat([]byte{0}, 50),
			},
		},
		{
			desc: "ARP, 2 VLANs: (PRI 0, DROP, ID 100), (PRI 1, ID 101)",
			b: append([]byte{
				0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
				0, 1, 0, 1, 0, 1,
				0x81, 0x00,
				0x10, 0x64,
				0x81, 0x00,
				0x20, 0x65,
				0x08, 0x06,
			}, bytes.Repeat([]byte{0}, 50)...),
			f: &Frame{
				DestinationHardwareAddr: Broadcast,
				SourceHardwareAddr:      net.HardwareAddr{0, 1, 0, 1, 0, 1},
				VLAN: []*VLAN{
					{
						DropEligible: true,
						ID:           100,
					},
					{
						Priority: 1,
						ID:       101,
					},
				},
				EtherType: EtherTypeARP,
				Payload:   bytes.Repeat([]byte{0}, 50),
			},
		},
	}

	for i, tt := range tests {
		f := new(Frame)
		if err := f.UnmarshalBinary(tt.b); err != nil {
			if want, got := tt.err, err; want != got {
				t.Fatalf("[%02d] test %q, unexpected error: %v != %v",
					i, tt.desc, want, got)
			}

			continue
		}

		if want, got := tt.f, f; !reflect.DeepEqual(want, got) {
			t.Fatalf("[%02d] test %q, unexpected Frame:\n- want: %v\n-  got: %v",
				i, tt.desc, want, got)
		}
	}
}
<commit_msg>ethernet_test: add benchmarks for Frame.{M,Unm}arshalBinary<commit_after>package ethernet

import (
	"bytes"
	"io"
	"net"
	"reflect"
	"testing"
)

func TestFrameMarshalBinary(t *testing.T) {
	var tests = []struct {
		desc string
		f    *Frame
		b    []byte
		err  error
	}{
		{
			desc: "VLAN priority too large",
			f: &Frame{
				VLAN: []*VLAN{{
					Priority: 8,
				}},
			},
			err: ErrInvalidVLAN,
		},
		{
			desc: "VLAN ID too large",
			f: &Frame{
				VLAN: []*VLAN{{
					ID: VLANMax,
				}},
			},
			err: ErrInvalidVLAN,
		},
		{
			desc: "IPv4, no VLANs",
			f: &Frame{
				DestinationHardwareAddr: net.HardwareAddr{0, 1, 0, 1, 0, 1},
				SourceHardwareAddr:      net.HardwareAddr{1, 0, 1, 0, 1, 0},
				EtherType:               EtherTypeIPv4,
				Payload:                 bytes.Repeat([]byte{0}, 50),
			},
			b: append([]byte{
				0, 1, 0, 1, 0, 1,
				1, 0, 1, 0, 1, 0,
				0x08, 0x00,
			}, bytes.Repeat([]byte{0}, 50)...),
		},
		{
			desc: "IPv6, 1 VLAN: PRI 1, ID 101",
			f: &Frame{
				DestinationHardwareAddr: net.HardwareAddr{1, 0, 1, 0, 1, 0},
				SourceHardwareAddr:      net.HardwareAddr{0, 1, 0, 1, 0, 1},
				VLAN: []*VLAN{{
					Priority: 1,
					ID:       101,
				}},
				EtherType: EtherTypeIPv6,
				Payload:   bytes.Repeat([]byte{0}, 50),
			},
			b: append([]byte{
				1, 0, 1, 0, 1, 0,
				0, 1, 0, 1, 0, 1,
				0x81, 0x00,
				0x20, 0x65,
				0x86, 0xDD,
			}, bytes.Repeat([]byte{0}, 50)...),
		},
		{
			desc: "ARP, 2 VLANs: (PRI 0, DROP, ID 100) (PRI 1, ID 101)",
			f: &Frame{
				DestinationHardwareAddr: Broadcast,
				SourceHardwareAddr:      net.HardwareAddr{0, 1, 0, 1, 0, 1},
				VLAN: []*VLAN{
					{
						DropEligible: true,
						ID:           100,
					},
					{
						Priority: 1,
						ID:       101,
					},
				},
				EtherType: EtherTypeARP,
				Payload:   bytes.Repeat([]byte{0}, 50),
			},
			b: append([]byte{
				0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
				0, 1, 0, 1, 0, 1,
				0x81, 0x00,
				0x10, 0x64,
				0x81, 0x00,
				0x20, 0x65,
				0x08, 0x06,
			}, bytes.Repeat([]byte{0}, 50)...),
		},
	}

	for i, tt := range tests {
		b, err := tt.f.MarshalBinary()
		if err != nil {
			if want, got := tt.err, err; want != got {
				t.Fatalf("[%02d] test %q, unexpected error: %v != %v",
					i, tt.desc, want, got)
			}

			continue
		}

		if want, got := tt.b, b; !bytes.Equal(want, got) {
			t.Fatalf("[%02d] test %q, unexpected Frame bytes:\n- want: %v\n-  got: %v",
				i, tt.desc, want, got)
		}
	}
}

func TestFrameUnmarshalBinary(t *testing.T) {
	var tests = []struct {
		desc string
		b    []byte
		f    *Frame
		err  error
	}{
		{
			desc: "nil buffer",
			err:  io.ErrUnexpectedEOF,
		},
		{
			desc: "short buffer",
			b:    bytes.Repeat([]byte{0}, 13),
			err:  io.ErrUnexpectedEOF,
		},
		{
			desc: "short payload, no VLANs",
			b:    bytes.Repeat([]byte{0}, 45),
			err:  io.ErrUnexpectedEOF,
		},
		{
			desc: "1 short VLAN",
			b: []byte{
				0, 0, 0, 0, 0, 0,
				0, 0, 0, 0, 0, 0,
				0x81, 0x00,
				0x00,
			},
			err: io.ErrUnexpectedEOF,
		},
		{
			desc: "VLAN ID too large",
			b: []byte{
				0, 0, 0, 0, 0, 0,
				0, 0, 0, 0, 0, 0,
				0x81, 0x00,
				0xff, 0xff,
			},
			err: ErrInvalidVLAN,
		},
		{
			desc: "short payload, 1 VLAN",
			b: append([]byte{
				0, 0, 0, 0, 0, 0,
				0, 0, 0, 0, 0, 0,
				0x81, 0x00,
				0x00, 0x00,
				0x00, 0x00,
			}, bytes.Repeat([]byte{0}, 41)...),
			err: io.ErrUnexpectedEOF,
		},
		{
			desc: "short payload, 2 VLANs",
			b: append([]byte{
				0, 0, 0, 0, 0, 0,
				0, 0, 0, 0, 0, 0,
				0x81, 0x00,
				0x00, 0x00,
				0x81, 0x00,
				0x00, 0x00,
				0x00, 0x00,
			}, bytes.Repeat([]byte{0}, 37)...),
			err: io.ErrUnexpectedEOF,
		},
		{
			desc: "0 VLANs detected, but 1 may have been present",
			b:    bytes.Repeat([]byte{0}, 56),
			f: &Frame{
				DestinationHardwareAddr: net.HardwareAddr{0, 0, 0, 0, 0, 0},
				SourceHardwareAddr:      net.HardwareAddr{0, 0, 0, 0, 0, 0},
				Payload:                 bytes.Repeat([]byte{0}, 42),
			},
		},
		{
			desc: "IPv4, no VLANs",
			b: append([]byte{
				0, 1, 0, 1, 0, 1,
				1, 0, 1, 0, 1, 0,
				0x08, 0x00,
			}, bytes.Repeat([]byte{0}, 50)...),
			f: &Frame{
				DestinationHardwareAddr: net.HardwareAddr{0, 1, 0, 1, 0, 1},
				SourceHardwareAddr:      net.HardwareAddr{1, 0, 1, 0, 1, 0},
				EtherType:               EtherTypeIPv4,
				Payload:                 bytes.Repeat([]byte{0}, 50),
			},
		},
		{
			desc: "IPv6, 1 VLAN: PRI 1, ID 101",
			b: append([]byte{
				1, 0, 1, 0, 1, 0,
				0, 1, 0, 1, 0, 1,
				0x81, 0x00,
				0x20, 0x65,
				0x86, 0xDD,
			}, bytes.Repeat([]byte{0}, 50)...),
			f: &Frame{
				DestinationHardwareAddr: net.HardwareAddr{1, 0, 1, 0, 1, 0},
				SourceHardwareAddr:      net.HardwareAddr{0, 1, 0, 1, 0, 1},
				VLAN: []*VLAN{{
					Priority: 1,
					ID:       101,
				}},
				EtherType: EtherTypeIPv6,
				Payload:   bytes.Repeat([]byte{0}, 50),
			},
		},
		{
			desc: "ARP, 2 VLANs: (PRI 0, DROP, ID 100), (PRI 1, ID 101)",
			b: append([]byte{
				0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
				0, 1, 0, 1, 0, 1,
				0x81, 0x00,
				0x10, 0x64,
				0x81, 0x00,
				0x20, 0x65,
				0x08, 0x06,
			}, bytes.Repeat([]byte{0}, 50)...),
			f: &Frame{
				DestinationHardwareAddr: Broadcast,
				SourceHardwareAddr:      net.HardwareAddr{0, 1, 0, 1, 0, 1},
				VLAN: []*VLAN{
					{
						DropEligible: true,
						ID:           100,
					},
					{
						Priority: 1,
						ID:       101,
					},
				},
				EtherType: EtherTypeARP,
				Payload:   bytes.Repeat([]byte{0}, 50),
			},
		},
	}

	for i, tt := range tests {
		f := new(Frame)
		if err := f.UnmarshalBinary(tt.b); err != nil {
			if want, got := tt.err, err; want != got {
				t.Fatalf("[%02d] test %q, unexpected error: %v != %v",
					i, tt.desc, want, got)
			}

			continue
		}

		if want, got := tt.f, f; !reflect.DeepEqual(want, got) {
			t.Fatalf("[%02d] test %q, unexpected Frame:\n- want: %v\n-  got: %v",
				i, tt.desc, want, got)
		}
	}
}

// Benchmarks for Frame.MarshalBinary with varying VLAN tags and payloads

func BenchmarkFrameMarshalBinary(b *testing.B) {
	f := &Frame{
		Payload: []byte{0, 1, 2, 3, 4},
	}

	benchmarkFrameMarshalBinary(b, f)
}

func BenchmarkFrameMarshalBinaryOneVLAN(b *testing.B) {
	f := &Frame{
		VLAN: []*VLAN{
			{
				Priority: PriorityBackground,
				ID:       10,
			},
		},
		Payload: []byte{0, 1, 2, 3, 4},
	}

	benchmarkFrameMarshalBinary(b, f)
}

func BenchmarkFrameMarshalBinaryTwoVLANs(b *testing.B) {
	f := &Frame{
		VLAN: []*VLAN{
			{
				Priority: PriorityBackground,
				ID:       10,
			},
			{
				Priority: PriorityBestEffort,
				ID:       20,
			},
		},
		Payload: []byte{0, 1, 2, 3, 4},
	}

	benchmarkFrameMarshalBinary(b, f)
}

func BenchmarkFrameMarshalBinaryJumboPayload(b *testing.B) {
	f := &Frame{
		Payload: make([]byte, 8192),
	}

	benchmarkFrameMarshalBinary(b, f)
}

func benchmarkFrameMarshalBinary(b *testing.B, f *Frame) {
	f.DestinationHardwareAddr = net.HardwareAddr{0xde, 0xad, 0xbe, 0xef, 0xde, 0xad}
	f.SourceHardwareAddr = net.HardwareAddr{0xad, 0xbe, 0xef, 0xde, 0xad, 0xde}

	b.ResetTimer()
	b.ReportAllocs()
	for i := 0; i < b.N; i++ {
		if _, err := f.MarshalBinary(); err != nil {
			b.Fatal(err)
		}
	}
}

// Benchmarks for Frame.UnmarshalBinary with varying VLAN tags and payloads

func BenchmarkFrameUnmarshalBinary(b *testing.B) {
	f := &Frame{
		Payload: []byte{0, 1, 2, 3, 4},
	}

	benchmarkFrameUnmarshalBinary(b, f)
}

func BenchmarkFrameUnmarshalBinaryOneVLAN(b *testing.B) {
	f := &Frame{
		VLAN: []*VLAN{
			{
				Priority: PriorityBackground,
				ID:       10,
			},
		},
		Payload: []byte{0, 1, 2, 3, 4},
	}

	benchmarkFrameUnmarshalBinary(b, f)
}

func BenchmarkFrameUnmarshalBinaryTwoVLANs(b *testing.B) {
	f := &Frame{
		VLAN: []*VLAN{
			{
				Priority: PriorityBackground,
				ID:       10,
			},
			{
				Priority: PriorityBestEffort,
				ID:       20,
			},
		},
		Payload: []byte{0, 1, 2, 3, 4},
	}

	benchmarkFrameUnmarshalBinary(b, f)
}

func BenchmarkFrameUnmarshalBinaryJumboPayload(b *testing.B) {
	f := &Frame{
		Payload: make([]byte, 8192),
	}

	benchmarkFrameUnmarshalBinary(b, f)
}

func benchmarkFrameUnmarshalBinary(b *testing.B, f *Frame) {
	f.DestinationHardwareAddr = net.HardwareAddr{0xde, 0xad, 0xbe, 0xef, 0xde, 0xad}
	f.SourceHardwareAddr = net.HardwareAddr{0xad, 0xbe, 0xef, 0xde, 0xad, 0xde}

	fb, err := f.MarshalBinary()
	if err != nil {
		b.Fatal(err)
	}

	b.ResetTimer()
	b.ReportAllocs()
	for i := 0; i < b.N; i++ {
		if err := f.UnmarshalBinary(fb); err != nil {
			b.Fatal(err)
		}
	}
}
<|endoftext|>


<commit_before>package eventemitter

import (
	"testing"
	"fmt"
)

// Struct for testing Embedding of EventEmitters
type Server struct {
	EventEmitter
}

func TestEmbedding(t *testing.T) {
	s := new(Server)

	// Don't forget to allocate the memory when
	// used as sub type.
	s.EventEmitter.Init()

	s.On("recv", func(event *Event) {
		event.Result = "bar"
	})

	e := <-s.Emit("recv")

	if res := e.Result.(string); res != "bar" {
		t.Errorf("Expected %s, got %s", "bar", res)
	}
}

func TestEmitReturnsChan(t *testing.T) {
	emitter := NewEventEmitter()

	emitter.On("foo", func(event *Event) {
	})

	e := <-emitter.Emit("foo")

	if e.Name != "foo" {
		t.Errorf("Expected event name %s, got %s", "foo", e.Name)
	}
}

func BenchmarkEmit(b *testing.B) {
	b.StopTimer()
	emitter := NewEventEmitter()

	for i := 0; i < 100; i++ {
		emitter.On("hello", func(event *Event) {
			event.Result = "Hello World " + event.Argv[0].(string)
		})
	}
	b.StartTimer()

	for i := 0; i < b.N; i++ {
		<- emitter.Emit("hello", "John")
	}
}

func ExampleEmit() {
	// Construct a new EventEmitter instance
	emitter := NewEventEmitter()

	emitter.On("hello", func(event *Event) {
		fmt.Println("Hello World")
	})

	emitter.On("hello", func(event *Event) {
		fmt.Println("Hello Hello World")
	})

	// Wait until all handlers have finished
	<- emitter.Emit("hello")
	// Output:
	// Hello World
	// Hello Hello World
}
<commit_msg>Formatted source files<commit_after>package eventemitter

import (
	"fmt"
	"testing"
)

// Struct for testing Embedding of EventEmitters
type Server struct {
	EventEmitter
}

func TestEmbedding(t *testing.T) {
	s := new(Server)

	// Don't forget to allocate the memory when
	// used as sub type.
	s.EventEmitter.Init()

	s.On("recv", func(event *Event) {
		event.Result = "bar"
	})

	e := <-s.Emit("recv")

	if res := e.Result.(string); res != "bar" {
		t.Errorf("Expected %s, got %s", "bar", res)
	}
}

func TestEmitReturnsChan(t *testing.T) {
	emitter := NewEventEmitter()

	emitter.On("foo", func(event *Event) {
	})

	e := <-emitter.Emit("foo")

	if e.Name != "foo" {
		t.Errorf("Expected event name %s, got %s", "foo", e.Name)
	}
}

func BenchmarkEmit(b *testing.B) {
	b.StopTimer()
	emitter := NewEventEmitter()

	for i := 0; i < 100; i++ {
		emitter.On("hello", func(event *Event) {
			event.Result = "Hello World " + event.Argv[0].(string)
		})
	}
	b.StartTimer()

	for i := 0; i < b.N; i++ {
		<-emitter.Emit("hello", "John")
	}
}

func ExampleEmit() {
	// Construct a new EventEmitter instance
	emitter := NewEventEmitter()

	emitter.On("hello", func(event *Event) {
		fmt.Println("Hello World")
	})

	emitter.On("hello", func(event *Event) {
		fmt.Println("Hello Hello World")
	})

	// Wait until all handlers have finished
	<-emitter.Emit("hello")
	// Output:
	// Hello World
	// Hello Hello World
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package options

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"time"

	"github.com/golang/glog"
	"github.com/spf13/pflag"

	"k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apiserver/pkg/authentication/authenticatorfactory"
	"k8s.io/apiserver/pkg/server"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/clientcmd"
	openapicommon "k8s.io/kube-openapi/pkg/common"
)

type RequestHeaderAuthenticationOptions struct {
	// ClientCAFile is the root certificate bundle to verify client certificates on incoming requests
	// before trusting usernames in headers.
	ClientCAFile string

	UsernameHeaders     []string
	GroupHeaders        []string
	ExtraHeaderPrefixes []string
	AllowedNames        []string
}

func (s *RequestHeaderAuthenticationOptions) AddFlags(fs *pflag.FlagSet) {
	if s == nil {
		return
	}

	fs.StringSliceVar(&s.UsernameHeaders, "requestheader-username-headers", s.UsernameHeaders, ""+
		"List of request headers to inspect for usernames. X-Remote-User is common.")

	fs.StringSliceVar(&s.GroupHeaders, "requestheader-group-headers", s.GroupHeaders, ""+
		"List of request headers to inspect for groups. X-Remote-Group is suggested.")

	fs.StringSliceVar(&s.ExtraHeaderPrefixes, "requestheader-extra-headers-prefix", s.ExtraHeaderPrefixes, ""+
		"List of request header prefixes to inspect. X-Remote-Extra- is suggested.")

	fs.StringVar(&s.ClientCAFile, "requestheader-client-ca-file", s.ClientCAFile, ""+
		"Root certificate bundle to use to verify client certificates on incoming requests "+
		"before trusting usernames in headers specified by --requestheader-username-headers. "+
		"WARNING: generally do not depend on authorization being already done for incoming requests.")

	fs.StringSliceVar(&s.AllowedNames, "requestheader-allowed-names", s.AllowedNames, ""+
		"List of client certificate common names to allow to provide usernames in headers "+
		"specified by --requestheader-username-headers. If empty, any client certificate validated "+
		"by the authorities in --requestheader-client-ca-file is allowed.")
}

// ToAuthenticationRequestHeaderConfig returns a RequestHeaderConfig config object for these options
// if necessary, nil otherwise.
func (s *RequestHeaderAuthenticationOptions) ToAuthenticationRequestHeaderConfig() *authenticatorfactory.RequestHeaderConfig {
	if len(s.ClientCAFile) == 0 {
		return nil
	}

	return &authenticatorfactory.RequestHeaderConfig{
		UsernameHeaders:     s.UsernameHeaders,
		GroupHeaders:        s.GroupHeaders,
		ExtraHeaderPrefixes: s.ExtraHeaderPrefixes,
		ClientCA:            s.ClientCAFile,
		AllowedClientNames:  s.AllowedNames,
	}
}

type ClientCertAuthenticationOptions struct {
	// ClientCA is the certificate bundle for all the signers that you'll recognize for incoming client certificates
	ClientCA string
}

func (s *ClientCertAuthenticationOptions) AddFlags(fs *pflag.FlagSet) {
	fs.StringVar(&s.ClientCA, "client-ca-file", s.ClientCA, ""+
		"If set, any request presenting a client certificate signed by one of "+
		"the authorities in the client-ca-file is authenticated with an identity "+
		"corresponding to the CommonName of the client certificate.")
}

// DelegatingAuthenticationOptions provides an easy way for composing API servers to delegate their authentication to
// the root kube API server.  The API federator will act as
// a front proxy and direction connections will be able to delegate to the core kube API server
type DelegatingAuthenticationOptions struct {
	// RemoteKubeConfigFile is the file to use to connect to a "normal" kube API server which hosts the
	// TokenAccessReview.authentication.k8s.io endpoint for checking tokens.
	RemoteKubeConfigFile string
	// RemoteKubeConfigFileOptional is specifying whether not specifying the kubeconfig or
	// a missing in-cluster config will be fatal.
	RemoteKubeConfigFileOptional bool

	// CacheTTL is the length of time that a token authentication answer will be cached.
	CacheTTL time.Duration

	ClientCert    ClientCertAuthenticationOptions
	RequestHeader RequestHeaderAuthenticationOptions

	SkipInClusterLookup bool
}

func NewDelegatingAuthenticationOptions() *DelegatingAuthenticationOptions {
	return &DelegatingAuthenticationOptions{
		// very low for responsiveness, but high enough to handle storms
		CacheTTL:   10 * time.Second,
		ClientCert: ClientCertAuthenticationOptions{},
		RequestHeader: RequestHeaderAuthenticationOptions{
			UsernameHeaders:     []string{"x-remote-user"},
			GroupHeaders:        []string{"x-remote-group"},
			ExtraHeaderPrefixes: []string{"x-remote-extra-"},
		},
	}
}

func (s *DelegatingAuthenticationOptions) Validate() []error {
	allErrors := []error{}
	return allErrors
}

func (s *DelegatingAuthenticationOptions) AddFlags(fs *pflag.FlagSet) {
	if s == nil {
		return
	}

	var optionalKubeConfigSentence string
	if s.RemoteKubeConfigFileOptional {
		optionalKubeConfigSentence = " This is optional. If empty, all token requests are considered to be anonymous and no client CA is looked up in the cluster."
	}
	fs.StringVar(&s.RemoteKubeConfigFile, "authentication-kubeconfig", s.RemoteKubeConfigFile, ""+
		"kubeconfig file pointing at the 'core' kubernetes server with enough rights to create "+
		"tokenaccessreviews.authentication.k8s.io."+optionalKubeConfigSentence)

	fs.DurationVar(&s.CacheTTL, "authentication-token-webhook-cache-ttl", s.CacheTTL,
		"The duration to cache responses from the webhook token authenticator.")

	s.ClientCert.AddFlags(fs)
	s.RequestHeader.AddFlags(fs)

	fs.BoolVar(&s.SkipInClusterLookup, "authentication-skip-lookup", s.SkipInClusterLookup, ""+
		"If false, the authentication-kubeconfig will be used to lookup missing authentication "+
		"configuration from the cluster.")
}

func (s *DelegatingAuthenticationOptions) ApplyTo(c *server.AuthenticationInfo, servingInfo *server.SecureServingInfo, openAPIConfig *openapicommon.Config) error {
	if s == nil {
		c.Authenticator = nil
		return nil
	}

	cfg := authenticatorfactory.DelegatingAuthenticatorConfig{
		Anonymous: true,
		CacheTTL:  s.CacheTTL,
	}

	client, err := s.getClient()
	if err != nil {
		return fmt.Errorf("failed to get delegated authentication kubeconfig: %v", err)
	}

	// configure token review
	if client != nil {
		cfg.TokenAccessReviewClient = client.AuthenticationV1beta1().TokenReviews()
	}

	// look into configmaps/external-apiserver-authentication for missing authn info
	if !s.SkipInClusterLookup {
		err := s.lookupMissingConfigInCluster(client)
		if err != nil {
			return err
		}
	}

	// configure AuthenticationInfo config
	if err = c.ApplyClientCert(s.ClientCert.ClientCA, servingInfo); err != nil {
		return fmt.Errorf("unable to load client CA file: %v", err)
	}
	cfg.RequestHeaderConfig = s.RequestHeader.ToAuthenticationRequestHeaderConfig()
	if err = c.ApplyClientCert(s.RequestHeader.ClientCAFile, servingInfo); err != nil {
		return fmt.Errorf("unable to load client CA file: %v", err)
	}

	// create authenticator
	authenticator, securityDefinitions, dynamicReloadFuncs, err := cfg.New()
	if err != nil {
		return err
	}
	c.Authenticator = authenticator
	if openAPIConfig != nil {
		openAPIConfig.SecurityDefinitions = securityDefinitions
	}
	c.SupportsBasicAuth = false
	if c.DynamicReloadFns == nil {
		c.DynamicReloadFns = map[string]server.PostStartHookFunc{}
	}
	for k, dynamicReloadFn := range dynamicReloadFuncs {
		c.DynamicReloadFns[k] = func(context server.PostStartHookContext) error {
			go dynamicReloadFn(context.StopCh)
			return nil
		}
	}

	return nil
}

const (
	authenticationConfigMapNamespace = metav1.NamespaceSystem
	// authenticationConfigMapName is the name of ConfigMap in the kube-system namespace holding the root certificate
	// bundle to use to verify client certificates on incoming requests before trusting usernames in headers specified
	// by --requestheader-username-headers. This is created in the cluster by the kube-apiserver.
	// "WARNING: generally do not depend on authorization being already done for incoming requests.")
	authenticationConfigMapName = "extension-apiserver-authentication"
	authenticationRoleName      = "extension-apiserver-authentication-reader"
)

func (s *DelegatingAuthenticationOptions) lookupMissingConfigInCluster(client kubernetes.Interface) error {
	if len(s.ClientCert.ClientCA) > 0 && len(s.RequestHeader.ClientCAFile) > 0 {
		return nil
	}
	if client == nil {
		if len(s.ClientCert.ClientCA) == 0 {
			glog.Warningf("No authentication-kubeconfig provided in order to lookup client-ca-file in configmap/%s in %s, so client certificate authentication won't work.", authenticationConfigMapName, authenticationConfigMapNamespace)
		}
		if len(s.RequestHeader.ClientCAFile) == 0 {
			glog.Warningf("No authentication-kubeconfig provided in order to lookup requestheader-client-ca-file in configmap/%s in %s, so request-header client certificate authentication won't work.", authenticationConfigMapName, authenticationConfigMapNamespace)
		}
		return nil
	}

	authConfigMap, err := client.CoreV1().ConfigMaps(authenticationConfigMapNamespace).Get(authenticationConfigMapName, metav1.GetOptions{})
	switch {
	case errors.IsNotFound(err):
		// ignore, authConfigMap is nil now
	case errors.IsForbidden(err):
		glog.Warningf("Unable to get configmap/%s in %s.  Usually fixed by "+
			"'kubectl create rolebinding -n %s ROLE_NAME --role=%s --serviceaccount=YOUR_NS:YOUR_SA'",
			authenticationConfigMapName, authenticationConfigMapNamespace, authenticationConfigMapNamespace, authenticationRoleName)
		return err
	case err != nil:
		return err
	}

	if len(s.ClientCert.ClientCA) == 0 {
		if authConfigMap != nil {
			opt, err := inClusterClientCA(authConfigMap)
			if err != nil {
				return err
			}
			if opt != nil {
				s.ClientCert = *opt
			}
		}
		if len(s.ClientCert.ClientCA) == 0 {
			glog.Warningf("Cluster doesn't provide client-ca-file in configmap/%s in %s, so client certificate authentication won't work.", authenticationConfigMapName, authenticationConfigMapNamespace)
		}
	}

	if len(s.RequestHeader.ClientCAFile) == 0 {
		if authConfigMap != nil {
			opt, err := inClusterRequestHeader(authConfigMap)
			if err != nil {
				return err
			}
			if opt != nil {
				s.RequestHeader = *opt
			}
		}
		if len(s.RequestHeader.ClientCAFile) == 0 {
			glog.Warningf("Cluster doesn't provide requestheader-client-ca-file in configmap/%s in %s, so request-header client certificate authentication won't work.", authenticationConfigMapName, authenticationConfigMapNamespace)
		}
	}

	return nil
}

func inClusterClientCA(authConfigMap *v1.ConfigMap) (*ClientCertAuthenticationOptions, error) {
	clientCA, ok := authConfigMap.Data["client-ca-file"]
	if !ok {
		// not having a client-ca is fine, return nil
		return nil, nil
	}

	f, err := ioutil.TempFile("", "client-ca-file")
	if err != nil {
		return nil, err
	}
	if err := ioutil.WriteFile(f.Name(), []byte(clientCA), 0600); err != nil {
		return nil, err
	}
	return &ClientCertAuthenticationOptions{ClientCA: f.Name()}, nil
}

func inClusterRequestHeader(authConfigMap *v1.ConfigMap) (*RequestHeaderAuthenticationOptions, error) {
	requestHeaderCA, ok := authConfigMap.Data["requestheader-client-ca-file"]
	if !ok {
		// not having a requestheader-client-ca is fine, return nil
		return nil, nil
	}

	f, err := ioutil.TempFile("", "requestheader-client-ca-file")
	if err != nil {
		return nil, err
	}
	if err := ioutil.WriteFile(f.Name(), []byte(requestHeaderCA), 0600); err != nil {
		return nil, err
	}
	usernameHeaders, err := deserializeStrings(authConfigMap.Data["requestheader-username-headers"])
	if err != nil {
		return nil, err
	}
	groupHeaders, err := deserializeStrings(authConfigMap.Data["requestheader-group-headers"])
	if err != nil {
		return nil, err
	}
	extraHeaderPrefixes, err := deserializeStrings(authConfigMap.Data["requestheader-extra-headers-prefix"])
	if err != nil {
		return nil, err
	}
	allowedNames, err := deserializeStrings(authConfigMap.Data["requestheader-allowed-names"])
	if err != nil {
		return nil, err
	}

	return &RequestHeaderAuthenticationOptions{
		UsernameHeaders:     usernameHeaders,
		GroupHeaders:        groupHeaders,
		ExtraHeaderPrefixes: extraHeaderPrefixes,
		ClientCAFile:        f.Name(),
		AllowedNames:        allowedNames,
	}, nil
}

func deserializeStrings(in string) ([]string, error) {
	if len(in) == 0 {
		return nil, nil
	}
	var ret []string
	if err := json.Unmarshal([]byte(in), &ret); err != nil {
		return nil, err
	}
	return ret, nil
}

// getClient returns a Kubernetes clientset. If s.RemoteKubeConfigFileOptional is true, nil will be returned
// if no kubeconfig is specified by the user and the in-cluster config is not found.
func (s *DelegatingAuthenticationOptions) getClient() (kubernetes.Interface, error) {
	var clientConfig *rest.Config
	var err error
	if len(s.RemoteKubeConfigFile) > 0 {
		loadingRules := &clientcmd.ClientConfigLoadingRules{ExplicitPath: s.RemoteKubeConfigFile}
		loader := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, &clientcmd.ConfigOverrides{})

		clientConfig, err = loader.ClientConfig()
	} else {
		// without the remote kubeconfig file, try to use the in-cluster config.  Most addon API servers will
		// use this path. If it is optional, ignore errors.
		clientConfig, err = rest.InClusterConfig()
		if err != nil && s.RemoteKubeConfigFileOptional {
			if err != rest.ErrNotInCluster {
				glog.Warningf("failed to read in-cluster kubeconfig for delegated authentication: %v", err)
			}
			return nil, nil
		}
	}
	if err != nil {
		return nil, fmt.Errorf("failed to get delegated authentication kubeconfig: %v", err)
	}

	// set high qps/burst limits since this will effectively limit API server responsiveness
	clientConfig.QPS = 200
	clientConfig.Burst = 400

	return kubernetes.NewForConfig(clientConfig)
}
<commit_msg>UPSTREAM: 69430: Populate ClientCA in delegating auth setup<commit_after>/*
Copyright 2016 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package options

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"time"

	"github.com/golang/glog"
	"github.com/spf13/pflag"

	"k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apiserver/pkg/authentication/authenticatorfactory"
	"k8s.io/apiserver/pkg/server"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/clientcmd"
	openapicommon "k8s.io/kube-openapi/pkg/common"
)

type RequestHeaderAuthenticationOptions struct {
	// ClientCAFile is the root certificate bundle to verify client certificates on incoming requests
	// before trusting usernames in headers.
	ClientCAFile string

	UsernameHeaders     []string
	GroupHeaders        []string
	ExtraHeaderPrefixes []string
	AllowedNames        []string
}

func (s *RequestHeaderAuthenticationOptions) AddFlags(fs *pflag.FlagSet) {
	if s == nil {
		return
	}

	fs.StringSliceVar(&s.UsernameHeaders, "requestheader-username-headers", s.UsernameHeaders, ""+
		"List of request headers to inspect for usernames. X-Remote-User is common.")

	fs.StringSliceVar(&s.GroupHeaders, "requestheader-group-headers", s.GroupHeaders, ""+
		"List of request headers to inspect for groups. X-Remote-Group is suggested.")

	fs.StringSliceVar(&s.ExtraHeaderPrefixes, "requestheader-extra-headers-prefix", s.ExtraHeaderPrefixes, ""+
		"List of request header prefixes to inspect. X-Remote-Extra- is suggested.")

	fs.StringVar(&s.ClientCAFile, "requestheader-client-ca-file", s.ClientCAFile, ""+
		"Root certificate bundle to use to verify client certificates on incoming requests "+
		"before trusting usernames in headers specified by --requestheader-username-headers. "+
		"WARNING: generally do not depend on authorization being already done for incoming requests.")

	fs.StringSliceVar(&s.AllowedNames, "requestheader-allowed-names", s.AllowedNames, ""+
		"List of client certificate common names to allow to provide usernames in headers "+
		"specified by --requestheader-username-headers. If empty, any client certificate validated "+
		"by the authorities in --requestheader-client-ca-file is allowed.")
}

// ToAuthenticationRequestHeaderConfig returns a RequestHeaderConfig config object for these options
// if necessary, nil otherwise.
func (s *RequestHeaderAuthenticationOptions) ToAuthenticationRequestHeaderConfig() *authenticatorfactory.RequestHeaderConfig {
	if len(s.ClientCAFile) == 0 {
		return nil
	}

	return &authenticatorfactory.RequestHeaderConfig{
		UsernameHeaders:     s.UsernameHeaders,
		GroupHeaders:        s.GroupHeaders,
		ExtraHeaderPrefixes: s.ExtraHeaderPrefixes,
		ClientCA:            s.ClientCAFile,
		AllowedClientNames:  s.AllowedNames,
	}
}

type ClientCertAuthenticationOptions struct {
	// ClientCA is the certificate bundle for all the signers that you'll recognize for incoming client certificates
	ClientCA string
}

func (s *ClientCertAuthenticationOptions) AddFlags(fs *pflag.FlagSet) {
	fs.StringVar(&s.ClientCA, "client-ca-file", s.ClientCA, ""+
		"If set, any request presenting a client certificate signed by one of "+
		"the authorities in the client-ca-file is authenticated with an identity "+
		"corresponding to the CommonName of the client certificate.")
}

// DelegatingAuthenticationOptions provides an easy way for composing API servers to delegate their authentication to
// the root kube API server.  The API federator will act as
// a front proxy and direction connections will be able to delegate to the core kube API server
type DelegatingAuthenticationOptions struct {
	// RemoteKubeConfigFile is the file to use to connect to a "normal" kube API server which hosts the
	// TokenAccessReview.authentication.k8s.io endpoint for checking tokens.
	RemoteKubeConfigFile string
	// RemoteKubeConfigFileOptional is specifying whether not specifying the kubeconfig or
	// a missing in-cluster config will be fatal.
	RemoteKubeConfigFileOptional bool

	// CacheTTL is the length of time that a token authentication answer will be cached.
	CacheTTL time.Duration

	ClientCert    ClientCertAuthenticationOptions
	RequestHeader RequestHeaderAuthenticationOptions

	SkipInClusterLookup bool
}

func NewDelegatingAuthenticationOptions() *DelegatingAuthenticationOptions {
	return &DelegatingAuthenticationOptions{
		// very low for responsiveness, but high enough to handle storms
		CacheTTL:   10 * time.Second,
		ClientCert: ClientCertAuthenticationOptions{},
		RequestHeader: RequestHeaderAuthenticationOptions{
			UsernameHeaders:     []string{"x-remote-user"},
			GroupHeaders:        []string{"x-remote-group"},
			ExtraHeaderPrefixes: []string{"x-remote-extra-"},
		},
	}
}

func (s *DelegatingAuthenticationOptions) Validate() []error {
	allErrors := []error{}
	return allErrors
}

func (s *DelegatingAuthenticationOptions) AddFlags(fs *pflag.FlagSet) {
	if s == nil {
		return
	}

	var optionalKubeConfigSentence string
	if s.RemoteKubeConfigFileOptional {
		optionalKubeConfigSentence = " This is optional. If empty, all token requests are considered to be anonymous and no client CA is looked up in the cluster."
	}
	fs.StringVar(&s.RemoteKubeConfigFile, "authentication-kubeconfig", s.RemoteKubeConfigFile, ""+
		"kubeconfig file pointing at the 'core' kubernetes server with enough rights to create "+
		"tokenaccessreviews.authentication.k8s.io."+optionalKubeConfigSentence)

	fs.DurationVar(&s.CacheTTL, "authentication-token-webhook-cache-ttl", s.CacheTTL,
		"The duration to cache responses from the webhook token authenticator.")

	s.ClientCert.AddFlags(fs)
	s.RequestHeader.AddFlags(fs)

	fs.BoolVar(&s.SkipInClusterLookup, "authentication-skip-lookup", s.SkipInClusterLookup, ""+
		"If false, the authentication-kubeconfig will be used to lookup missing authentication "+
		"configuration from the cluster.")
}

func (s *DelegatingAuthenticationOptions) ApplyTo(c *server.AuthenticationInfo, servingInfo *server.SecureServingInfo, openAPIConfig *openapicommon.Config) error {
	if s == nil {
		c.Authenticator = nil
		return nil
	}

	cfg := authenticatorfactory.DelegatingAuthenticatorConfig{
		Anonymous: true,
		CacheTTL:  s.CacheTTL,
	}

	client, err := s.getClient()
	if err != nil {
		return fmt.Errorf("failed to get delegated authentication kubeconfig: %v", err)
	}

	// configure token review
	if client != nil {
		cfg.TokenAccessReviewClient = client.AuthenticationV1beta1().TokenReviews()
	}

	// look into configmaps/external-apiserver-authentication for missing authn info
	if !s.SkipInClusterLookup {
		err := s.lookupMissingConfigInCluster(client)
		if err != nil {
			return err
		}
	}

	// configure AuthenticationInfo config
	cfg.ClientCAFile = s.ClientCert.ClientCA
	if err = c.ApplyClientCert(s.ClientCert.ClientCA, servingInfo); err != nil {
		return fmt.Errorf("unable to load client CA file: %v", err)
	}
	cfg.RequestHeaderConfig = s.RequestHeader.ToAuthenticationRequestHeaderConfig()
	if err = c.ApplyClientCert(s.RequestHeader.ClientCAFile, servingInfo); err != nil {
		return fmt.Errorf("unable to load client CA file: %v", err)
	}

	// create authenticator
	authenticator, securityDefinitions, dynamicReloadFuncs, err := cfg.New()
	if err != nil {
		return err
	}
	c.Authenticator = authenticator
	if openAPIConfig != nil {
		openAPIConfig.SecurityDefinitions = securityDefinitions
	}
	c.SupportsBasicAuth = false
	if c.DynamicReloadFns == nil {
		c.DynamicReloadFns = map[string]server.PostStartHookFunc{}
	}
	for k, dynamicReloadFn := range dynamicReloadFuncs {
		c.DynamicReloadFns[k] = func(context server.PostStartHookContext) error {
			go dynamicReloadFn(context.StopCh)
			return nil
		}
	}

	return nil
}

const (
	authenticationConfigMapNamespace = metav1.NamespaceSystem
	// authenticationConfigMapName is the name of ConfigMap in the kube-system namespace holding the root certificate
	// bundle to use to verify client certificates on incoming requests before trusting usernames in headers specified
	// by --requestheader-username-headers. This is created in the cluster by the kube-apiserver.
	// "WARNING: generally do not depend on authorization being already done for incoming requests.")
	authenticationConfigMapName = "extension-apiserver-authentication"
	authenticationRoleName      = "extension-apiserver-authentication-reader"
)

func (s *DelegatingAuthenticationOptions) lookupMissingConfigInCluster(client kubernetes.Interface) error {
	if len(s.ClientCert.ClientCA) > 0 && len(s.RequestHeader.ClientCAFile) > 0 {
		return nil
	}
	if client == nil {
		if len(s.ClientCert.ClientCA) == 0 {
			glog.Warningf("No authentication-kubeconfig provided in order to lookup client-ca-file in configmap/%s in %s, so client certificate authentication won't work.", authenticationConfigMapName, authenticationConfigMapNamespace)
		}
		if len(s.RequestHeader.ClientCAFile) == 0 {
			glog.Warningf("No authentication-kubeconfig provided in order to lookup requestheader-client-ca-file in configmap/%s in %s, so request-header client certificate authentication won't work.", authenticationConfigMapName, authenticationConfigMapNamespace)
		}
		return nil
	}

	authConfigMap, err := client.CoreV1().ConfigMaps(authenticationConfigMapNamespace).Get(authenticationConfigMapName, metav1.GetOptions{})
	switch {
	case errors.IsNotFound(err):
		// ignore, authConfigMap is nil now
	case errors.IsForbidden(err):
		glog.Warningf("Unable to get configmap/%s in %s.  Usually fixed by "+
			"'kubectl create rolebinding -n %s ROLE_NAME --role=%s --serviceaccount=YOUR_NS:YOUR_SA'",
			authenticationConfigMapName, authenticationConfigMapNamespace, authenticationConfigMapNamespace, authenticationRoleName)
		return err
	case err != nil:
		return err
	}

	if len(s.ClientCert.ClientCA) == 0 {
		if authConfigMap != nil {
			opt, err := inClusterClientCA(authConfigMap)
			if err != nil {
				return err
			}
			if opt != nil {
				s.ClientCert = *opt
			}
		}
		if len(s.ClientCert.ClientCA) == 0 {
			glog.Warningf("Cluster doesn't provide client-ca-file in configmap/%s in %s, so client certificate authentication won't work.", authenticationConfigMapName, authenticationConfigMapNamespace)
		}
	}

	if len(s.RequestHeader.ClientCAFile) == 0 {
		if authConfigMap != nil {
			opt, err := inClusterRequestHeader(authConfigMap)
			if err != nil {
				return err
			}
			if opt != nil {
				s.RequestHeader = *opt
			}
		}
		if len(s.RequestHeader.ClientCAFile) == 0 {
			glog.Warningf("Cluster doesn't provide requestheader-client-ca-file in configmap/%s in %s, so request-header client certificate authentication won't work.", authenticationConfigMapName, authenticationConfigMapNamespace)
		}
	}

	return nil
}

func inClusterClientCA(authConfigMap *v1.ConfigMap) (*ClientCertAuthenticationOptions, error) {
	clientCA, ok := authConfigMap.Data["client-ca-file"]
	if !ok {
		// not having a client-ca is fine, return nil
		return nil, nil
	}

	f, err := ioutil.TempFile("", "client-ca-file")
	if err != nil {
		return nil, err
	}
	if err := ioutil.WriteFile(f.Name(), []byte(clientCA), 0600); err != nil {
		return nil, err
	}
	return &ClientCertAuthenticationOptions{ClientCA: f.Name()}, nil
}

func inClusterRequestHeader(authConfigMap *v1.ConfigMap) (*RequestHeaderAuthenticationOptions, error) {
	requestHeaderCA, ok := authConfigMap.Data["requestheader-client-ca-file"]
	if !ok {
		// not having a requestheader-client-ca is fine, return nil
		return nil, nil
	}

	f, err := ioutil.TempFile("", "requestheader-client-ca-file")
	if err != nil {
		return nil, err
	}
	if err := ioutil.WriteFile(f.Name(), []byte(requestHeaderCA), 0600); err != nil {
		return nil, err
	}
	usernameHeaders, err := deserializeStrings(authConfigMap.Data["requestheader-username-headers"])
	if err != nil {
		return nil, err
	}
	groupHeaders, err := deserializeStrings(authConfigMap.Data["requestheader-group-headers"])
	if err != nil {
		return nil, err
	}
	extraHeaderPrefixes, err := deserializeStrings(authConfigMap.Data["requestheader-extra-headers-prefix"])
	if err != nil {
		return nil, err
	}
	allowedNames, err := deserializeStrings(authConfigMap.Data["requestheader-allowed-names"])
	if err != nil {
		return nil, err
	}

	return &RequestHeaderAuthenticationOptions{
		UsernameHeaders:     usernameHeaders,
		GroupHeaders:        groupHeaders,
		ExtraHeaderPrefixes: extraHeaderPrefixes,
		ClientCAFile:        f.Name(),
		AllowedNames:        allowedNames,
	}, nil
}

func deserializeStrings(in string) ([]string, error) {
	if len(in) == 0 {
		return nil, nil
	}
	var ret []string
	if err := json.Unmarshal([]byte(in), &ret); err != nil {
		return nil, err
	}
	return ret, nil
}

// getClient returns a Kubernetes clientset. If s.RemoteKubeConfigFileOptional is true, nil will be returned
// if no kubeconfig is specified by the user and the in-cluster config is not found.
func (s *DelegatingAuthenticationOptions) getClient() (kubernetes.Interface, error) {
	var clientConfig *rest.Config
	var err error
	if len(s.RemoteKubeConfigFile) > 0 {
		loadingRules := &clientcmd.ClientConfigLoadingRules{ExplicitPath: s.RemoteKubeConfigFile}
		loader := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, &clientcmd.ConfigOverrides{})

		clientConfig, err = loader.ClientConfig()
	} else {
		// without the remote kubeconfig file, try to use the in-cluster config.  Most addon API servers will
		// use this path. If it is optional, ignore errors.
		clientConfig, err = rest.InClusterConfig()
		if err != nil && s.RemoteKubeConfigFileOptional {
			if err != rest.ErrNotInCluster {
				glog.Warningf("failed to read in-cluster kubeconfig for delegated authentication: %v", err)
			}
			return nil, nil
		}
	}
	if err != nil {
		return nil, fmt.Errorf("failed to get delegated authentication kubeconfig: %v", err)
	}

	// set high qps/burst limits since this will effectively limit API server responsiveness
	clientConfig.QPS = 200
	clientConfig.Burst = 400

	return kubernetes.NewForConfig(clientConfig)
}
<|endoftext|>