<commit_before>// Copyright 2016, RadiantBlue Technologies, 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 systest

import (
	"bytes"
	"io"
	"log"
	"net/http"
	"os"
	"strconv"
	"testing"
	"time"

	"encoding/json"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/suite"
	"github.com/venicegeo/pz-gocommon/gocommon"
	pzsyslog "github.com/venicegeo/pz-gocommon/syslog"
)

func sleep(n int) {
	time.Sleep(time.Second * time.Duration(n))
}

type LoggerTester struct {
	suite.Suite

	logWriter   pzsyslog.Writer
	httpWriter  *pzsyslog.HttpWriter // just a typed copy of logWriter
	auditWriter pzsyslog.Writer

	logger    *pzsyslog.Logger
	apiKey    string
	apiHost   string
	loggerUrl string

	mssgHostName    string
	mssgApplication string
	mssgProcess     string
	mmsgSeverity    pzsyslog.Severity
}

func (suite *LoggerTester) setupFixture() {
	t := suite.T()
	assert := assert.New(t)

	var err error

	suite.apiHost, err = piazza.GetApiServer()
	if err != nil {
		assert.FailNow(err.Error())
	}

	// note that we are NOT using the gateway
	suite.loggerUrl, err = piazza.GetPiazzaServiceUrl(piazza.PzLogger)
	assert.NoError(err)

	suite.apiKey, err = piazza.GetApiKey(suite.apiHost)
	assert.NoError(err)

	suite.httpWriter, err = pzsyslog.NewHttpWriter(suite.loggerUrl, suite.apiKey)
	suite.logWriter = suite.httpWriter
	assert.NoError(err)

	suite.auditWriter, err = pzsyslog.NewHttpWriter(suite.loggerUrl, suite.apiKey)

	uniq := strconv.FormatInt(time.Now().Unix(), 10)

	suite.mssgHostName, err = piazza.GetExternalIP()
	assert.NoError(err)
	suite.mssgApplication = "pzlogger-systest" + "/" + uniq
	suite.mssgProcess = strconv.Itoa(os.Getpid())
	suite.mmsgSeverity = pzsyslog.Informational

	suite.logger = pzsyslog.NewLogger(suite.logWriter, suite.auditWriter, suite.mssgApplication)
}

func (suite *LoggerTester) teardownFixture() {
	t := suite.T()
	assert := assert.New(t)

	err := suite.logWriter.Close()
	assert.NoError(err)
}

func TestRunSuite(t *testing.T) {
	s := &LoggerTester{}
	suite.Run(t, s)
}

func (suite *LoggerTester) verifyMessage(expected string) {
	format := &piazza.JsonPagination{
		PerPage: 50,
		Page:    0,
		SortBy:  "timeStamp",
		Order:   piazza.SortOrderDescending,
	}
	params := &piazza.HttpQueryParams{}
	suite.verifyMessageF(format, params, expected)
}

func (suite *LoggerTester) verifyMessageF(
	format *piazza.JsonPagination,
	params *piazza.HttpQueryParams,
	expected string,
) {
	t := suite.T()
	assert := assert.New(t)

	ms, _, err := suite.httpWriter.GetMessages(format, params)
	assert.NoError(err)
	assert.Len(ms, format.PerPage)

	ok := false
	for _, m := range ms {
		log.Printf("%s  %s", m.TimeStamp.String(), m.Message)
		if m.Message == expected {
			ok = true
			break
		}
	}
	assert.True(ok)
}

func (suite *LoggerTester) getVersion() (*piazza.Version, error) {
	h := &piazza.Http{BaseUrl: suite.loggerUrl}
	jresp := h.PzGet("/version")
	if jresp.IsError() {
		return nil, jresp.ToError()
	}

	var version piazza.Version
	err := jresp.ExtractData(&version)
	if err != nil {
		return nil, err
	}

	return &version, nil
}

func (suite *LoggerTester) getStats(output interface{}) error {
	h := &piazza.Http{BaseUrl: suite.loggerUrl}

	jresp := h.PzGet("/admin/stats")
	if jresp.IsError() {
		return jresp.ToError()
	}

	return jresp.ExtractData(output)
}

func (suite *LoggerTester) makeMessage(text string) *pzsyslog.Message {
	t := suite.T()
	assert := assert.New(t)

	var err error

	m := pzsyslog.NewMessage()
	log.Printf("%s", m.TimeStamp)
	m.Message = text
	m.HostName, err = piazza.GetExternalIP()
	assert.NoError(err)
	m.Application = "pzlogger-systest"
	m.Process = strconv.Itoa(os.Getpid())
	m.Severity = pzsyslog.Informational

	return m
}

func (suite *LoggerTester) Test00Version() {
	t := suite.T()
	assert := assert.New(t)

	suite.setupFixture()
	defer suite.teardownFixture()

	version, err := suite.getVersion()
	assert.NoError(err)
	assert.EqualValues("1.0.0", version.Version)
}

func (suite *LoggerTester) Test01RawGet() {
	t := suite.T()
	assert := assert.New(t)

	suite.setupFixture()
	defer suite.teardownFixture()

	resp, err := http.Get(suite.loggerUrl + "/syslog")
	log.Printf("-- %#v --", resp)
	assert.NoError(err)
	assert.True(resp.ContentLength > 0)

	raw := make([]byte, resp.ContentLength)
	_, err = io.ReadFull(resp.Body, raw)
	defer func() {
		err = resp.Body.Close()
		assert.NoError(err)
	}()
	if err != nil && err != io.EOF {
		assert.NoError(err)
	}

	assert.Equal(200, resp.StatusCode)
}

func (suite *LoggerTester) Test02RawPost() {
	t := suite.T()
	assert := assert.New(t)

	suite.setupFixture()
	defer suite.teardownFixture()

	mssg := suite.makeMessage("Test02")

	jsn, err := json.Marshal(mssg)
	reader := bytes.NewReader(jsn)

	resp, err := http.Post(suite.loggerUrl+"/syslog",
		piazza.ContentTypeJSON, reader)
	assert.NoError(err)

	raw := make([]byte, resp.ContentLength)
	_, err = io.ReadFull(resp.Body, raw)
	defer func() {
		err = resp.Body.Close()
		assert.NoError(err)
	}()
	if err != nil && err != io.EOF {
		assert.NoError(err)
	}

	assert.Equal(200, resp.StatusCode)
}

func (suite *LoggerTester) Test03Get() {
	t := suite.T()
	assert := assert.New(t)

	suite.setupFixture()
	defer suite.teardownFixture()

	format := &piazza.JsonPagination{
		PerPage: 12,
		Page:    0,
		Order:   piazza.SortOrderDescending,
		SortBy:  "hostName",
	}
	ms, _, err := suite.httpWriter.GetMessages(format, nil)
	assert.NoError(err)
	assert.Len(ms, format.PerPage)

	assert.False(time.Time(ms[0].TimeStamp).IsZero())
}

func (suite *LoggerTester) Test04Post() {
	t := suite.T()
	assert := assert.New(t)

	suite.setupFixture()
	defer suite.teardownFixture()

	var err error

	nowz := piazza.NewTimeStamp()
	keyz := "KEYZ KEYZ KEYZ " + nowz.String()

	mssgz := suite.makeMessage(keyz)
	mssgz.TimeStamp = nowz

	err = suite.httpWriter.Write(mssgz)
	assert.NoError(err)

	// allow ES to catch up
	sleep(2)

	suite.verifyMessage(keyz)
}

func (suite *LoggerTester) Test05Logger() {
	t := suite.T()
	assert := assert.New(t)

	suite.setupFixture()
	defer suite.teardownFixture()

	uniq := "Test05/" + time.Now().String()

	err := suite.logger.Info(uniq)
	assert.NoError(err)

	sleep(2)

	suite.verifyMessage(uniq)
}

func (suite *LoggerTester) Test06Admin() {
	t := suite.T()
	assert := assert.New(t)

	suite.setupFixture()
	defer suite.teardownFixture()

	output := map[string]interface{}{}
	err := suite.getStats(&output)
	assert.NoError(err)
	assert.NotZero(output["numMessages"])
}

func (suite *LoggerTester) Test07Pagination() {
	t := suite.T()
	assert := assert.New(t)

	suite.setupFixture()
	defer suite.teardownFixture()

	format := &piazza.JsonPagination{
		PerPage: 10,
		Page:    0,
		SortBy:  "timeStamp",
		Order:   piazza.SortOrderAscending,
	}
	params := &piazza.HttpQueryParams{}

	// check per-page
	{
		format.PerPage = 17
		ms, _, err := suite.httpWriter.GetMessages(format, params)
		assert.NoError(err)
		assert.Len(ms, 17)
	}

	// check sort order
	{
		format.PerPage = 10
		format.Order = piazza.SortOrderAscending
		ms, _, err := suite.httpWriter.GetMessages(format, params)
		assert.NoError(err)
		last := len(ms) - 1
		assert.True(last <= 9)

		// we can't check "strictly before", because two timeStamps might be the same
		t0 := time.Time(ms[0].TimeStamp)
		tlast := time.Time(ms[last].TimeStamp)
		isBefore := t0.Before(tlast)
		isEqual := t0.Equal(tlast)
		assert.True(isBefore || isEqual)

		format.Order = piazza.SortOrderDescending
		ms, _, err = suite.httpWriter.GetMessages(format, params)
		assert.NoError(err)
		last = len(ms) - 1
		assert.True(last <= 9)

		t0 = time.Time(ms[0].TimeStamp)
		tlast = time.Time(ms[last].TimeStamp)
		isAfter := t0.After(tlast)
		isEqual = t0.Equal(tlast)
		assert.True(isAfter || isEqual)
	}

	// check sort-by
	{
		format.Order = piazza.SortOrderAscending
		format.SortBy = "severity"
		format.PerPage = 100
		format.Page = 0
		ms, _, err := suite.httpWriter.GetMessages(format, params)
		assert.NoError(err)

		last := len(ms) - 1
		for i := 0; i < last; i++ {
			a, b := ms[i].Severity, ms[i+1].Severity
			isSameOrBefore := (a <= b)
			assert.True(isSameOrBefore)
		}
	}
}

func (suite *LoggerTester) Test08Params() {
	t := suite.T()
	assert := assert.New(t)

	suite.setupFixture()
	defer suite.teardownFixture()

	uniq := "Test08/" + strconv.Itoa(time.Now().Nanosecond())

	delta := time.Duration(10 * time.Second)
	tstart := time.Now().Add(-delta).UTC()

	err := suite.logger.Information(uniq)
	assert.NoError(err)

	tend := time.Now().Add(delta).UTC()

	sleep(1)

	format := &piazza.JsonPagination{
		PerPage: 256,
		Page:    0,
		Order:   piazza.SortOrderDescending,
		SortBy:  "timeStamp",
	}

	// test date range params
	{

		params := &piazza.HttpQueryParams{}
		params.AddTime("after", tstart)
		params.AddTime("before", tend)

		msgs, cnt, err := suite.httpWriter.GetMessages(format, params)
		assert.NoError(err)

		assert.True(cnt >= 1)
		assert.True(len(msgs) >= 1)
	}

	// test service param
	{
		params := &piazza.HttpQueryParams{}
		params.AddString("service", suite.mssgApplication)

		msgs, _, err := suite.httpWriter.GetMessages(format, params)
		assert.NoError(err)

		assert.Len(msgs, 1)
	}

	// test contains param
	{
		params := &piazza.HttpQueryParams{}
		params.AddString("contains", suite.mssgHostName)

		msgs, _, err := suite.httpWriter.GetMessages(format, params)
		assert.NoError(err)

		assert.True(len(msgs) >= 1)
	}
}
<commit_msg>remove debugging<commit_after>// Copyright 2016, RadiantBlue Technologies, 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 systest

import (
	"bytes"
	"io"
	"net/http"
	"os"
	"strconv"
	"testing"
	"time"

	"encoding/json"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/suite"
	"github.com/venicegeo/pz-gocommon/gocommon"
	pzsyslog "github.com/venicegeo/pz-gocommon/syslog"
)

func sleep(n int) {
	time.Sleep(time.Second * time.Duration(n))
}

type LoggerTester struct {
	suite.Suite

	logWriter   pzsyslog.Writer
	httpWriter  *pzsyslog.HttpWriter // just a typed copy of logWriter
	auditWriter pzsyslog.Writer

	logger    *pzsyslog.Logger
	apiKey    string
	apiHost   string
	loggerUrl string

	mssgHostName    string
	mssgApplication string
	mssgProcess     string
	mmsgSeverity    pzsyslog.Severity
}

func (suite *LoggerTester) setupFixture() {
	t := suite.T()
	assert := assert.New(t)

	var err error

	suite.apiHost, err = piazza.GetApiServer()
	if err != nil {
		assert.FailNow(err.Error())
	}

	// note that we are NOT using the gateway
	suite.loggerUrl, err = piazza.GetPiazzaServiceUrl(piazza.PzLogger)
	assert.NoError(err)

	suite.apiKey, err = piazza.GetApiKey(suite.apiHost)
	assert.NoError(err)

	suite.httpWriter, err = pzsyslog.NewHttpWriter(suite.loggerUrl, suite.apiKey)
	suite.logWriter = suite.httpWriter
	assert.NoError(err)

	suite.auditWriter, err = pzsyslog.NewHttpWriter(suite.loggerUrl, suite.apiKey)

	uniq := strconv.FormatInt(time.Now().Unix(), 10)

	suite.mssgHostName, err = piazza.GetExternalIP()
	assert.NoError(err)
	suite.mssgApplication = "pzlogger-systest" + "/" + uniq
	suite.mssgProcess = strconv.Itoa(os.Getpid())
	suite.mmsgSeverity = pzsyslog.Informational

	suite.logger = pzsyslog.NewLogger(suite.logWriter, suite.auditWriter, suite.mssgApplication)
}

func (suite *LoggerTester) teardownFixture() {
	t := suite.T()
	assert := assert.New(t)

	err := suite.logWriter.Close()
	assert.NoError(err)
}

func TestRunSuite(t *testing.T) {
	s := &LoggerTester{}
	suite.Run(t, s)
}

func (suite *LoggerTester) verifyMessage(expected string) {
	format := &piazza.JsonPagination{
		PerPage: 500, // has to be this high, in case logger is under high load
		Page:    0,
		SortBy:  "timeStamp",
		Order:   piazza.SortOrderDescending,
	}
	params := &piazza.HttpQueryParams{}
	suite.verifyMessageF(format, params, expected)
}

func (suite *LoggerTester) verifyMessageF(
	format *piazza.JsonPagination,
	params *piazza.HttpQueryParams,
	expected string,
) {
	t := suite.T()
	assert := assert.New(t)

	ms, _, err := suite.httpWriter.GetMessages(format, params)
	assert.NoError(err)
	assert.Len(ms, format.PerPage)

	ok := false
	for _, m := range ms {
		if m.Message == expected {
			ok = true
			break
		}
	}
	assert.True(ok)
}

func (suite *LoggerTester) getVersion() (*piazza.Version, error) {
	h := &piazza.Http{BaseUrl: suite.loggerUrl}
	jresp := h.PzGet("/version")
	if jresp.IsError() {
		return nil, jresp.ToError()
	}

	var version piazza.Version
	err := jresp.ExtractData(&version)
	if err != nil {
		return nil, err
	}

	return &version, nil
}

func (suite *LoggerTester) getStats(output interface{}) error {
	h := &piazza.Http{BaseUrl: suite.loggerUrl}

	jresp := h.PzGet("/admin/stats")
	if jresp.IsError() {
		return jresp.ToError()
	}

	return jresp.ExtractData(output)
}

func (suite *LoggerTester) makeMessage(text string) *pzsyslog.Message {
	t := suite.T()
	assert := assert.New(t)

	var err error

	m := pzsyslog.NewMessage()
	m.Message = text
	m.HostName, err = piazza.GetExternalIP()
	assert.NoError(err)
	m.Application = "pzlogger-systest"
	m.Process = strconv.Itoa(os.Getpid())
	m.Severity = pzsyslog.Informational

	return m
}

func (suite *LoggerTester) Test00Version() {
	t := suite.T()
	assert := assert.New(t)

	suite.setupFixture()
	defer suite.teardownFixture()

	version, err := suite.getVersion()
	assert.NoError(err)
	assert.EqualValues("1.0.0", version.Version)
}

func (suite *LoggerTester) Test01RawGet() {
	t := suite.T()
	assert := assert.New(t)

	suite.setupFixture()
	defer suite.teardownFixture()

	resp, err := http.Get(suite.loggerUrl + "/syslog")
	assert.NoError(err)
	assert.True(resp.ContentLength > 0)

	raw := make([]byte, resp.ContentLength)
	_, err = io.ReadFull(resp.Body, raw)
	defer func() {
		err = resp.Body.Close()
		assert.NoError(err)
	}()
	if err != nil && err != io.EOF {
		assert.NoError(err)
	}

	assert.Equal(200, resp.StatusCode)
}

func (suite *LoggerTester) Test02RawPost() {
	t := suite.T()
	assert := assert.New(t)

	suite.setupFixture()
	defer suite.teardownFixture()

	mssg := suite.makeMessage("Test02")

	jsn, err := json.Marshal(mssg)
	reader := bytes.NewReader(jsn)

	resp, err := http.Post(suite.loggerUrl+"/syslog",
		piazza.ContentTypeJSON, reader)
	assert.NoError(err)

	raw := make([]byte, resp.ContentLength)
	_, err = io.ReadFull(resp.Body, raw)
	defer func() {
		err = resp.Body.Close()
		assert.NoError(err)
	}()
	if err != nil && err != io.EOF {
		assert.NoError(err)
	}

	assert.Equal(200, resp.StatusCode)
}

func (suite *LoggerTester) Test03Get() {
	t := suite.T()
	assert := assert.New(t)

	suite.setupFixture()
	defer suite.teardownFixture()

	format := &piazza.JsonPagination{
		PerPage: 12,
		Page:    0,
		Order:   piazza.SortOrderDescending,
		SortBy:  "hostName",
	}
	ms, _, err := suite.httpWriter.GetMessages(format, nil)
	assert.NoError(err)
	assert.Len(ms, format.PerPage)

	assert.False(time.Time(ms[0].TimeStamp).IsZero())
}

func (suite *LoggerTester) Test04Post() {
	t := suite.T()
	assert := assert.New(t)

	suite.setupFixture()
	defer suite.teardownFixture()

	var err error

	nowz := piazza.NewTimeStamp()
	keyz := "KEYZ KEYZ KEYZ " + nowz.String()

	mssgz := suite.makeMessage(keyz)
	mssgz.TimeStamp = nowz

	err = suite.httpWriter.Write(mssgz)
	assert.NoError(err)

	// allow ES to catch up
	sleep(2)

	suite.verifyMessage(keyz)
}

func (suite *LoggerTester) Test05Logger() {
	t := suite.T()
	assert := assert.New(t)

	suite.setupFixture()
	defer suite.teardownFixture()

	uniq := "Test05/" + time.Now().String()

	err := suite.logger.Info(uniq)
	assert.NoError(err)

	sleep(2)

	suite.verifyMessage(uniq)
}

func (suite *LoggerTester) Test06Admin() {
	t := suite.T()
	assert := assert.New(t)

	suite.setupFixture()
	defer suite.teardownFixture()

	output := map[string]interface{}{}
	err := suite.getStats(&output)
	assert.NoError(err)
	assert.NotZero(output["numMessages"])
}

func (suite *LoggerTester) Test07Pagination() {
	t := suite.T()
	assert := assert.New(t)

	suite.setupFixture()
	defer suite.teardownFixture()

	format := &piazza.JsonPagination{
		PerPage: 10,
		Page:    0,
		SortBy:  "timeStamp",
		Order:   piazza.SortOrderAscending,
	}
	params := &piazza.HttpQueryParams{}

	// check per-page
	{
		format.PerPage = 17
		ms, _, err := suite.httpWriter.GetMessages(format, params)
		assert.NoError(err)
		assert.Len(ms, 17)
	}

	// check sort order
	{
		format.PerPage = 10
		format.Order = piazza.SortOrderAscending
		ms, _, err := suite.httpWriter.GetMessages(format, params)
		assert.NoError(err)
		last := len(ms) - 1
		assert.True(last <= 9)

		// we can't check "strictly before", because two timeStamps might be the same
		t0 := time.Time(ms[0].TimeStamp)
		tlast := time.Time(ms[last].TimeStamp)
		isBefore := t0.Before(tlast)
		isEqual := t0.Equal(tlast)
		assert.True(isBefore || isEqual)

		format.Order = piazza.SortOrderDescending
		ms, _, err = suite.httpWriter.GetMessages(format, params)
		assert.NoError(err)
		last = len(ms) - 1
		assert.True(last <= 9)

		t0 = time.Time(ms[0].TimeStamp)
		tlast = time.Time(ms[last].TimeStamp)
		isAfter := t0.After(tlast)
		isEqual = t0.Equal(tlast)
		assert.True(isAfter || isEqual)
	}

	// check sort-by
	{
		format.Order = piazza.SortOrderAscending
		format.SortBy = "severity"
		format.PerPage = 100
		format.Page = 0
		ms, _, err := suite.httpWriter.GetMessages(format, params)
		assert.NoError(err)

		last := len(ms) - 1
		for i := 0; i < last; i++ {
			a, b := ms[i].Severity, ms[i+1].Severity
			isSameOrBefore := (a <= b)
			assert.True(isSameOrBefore)
		}
	}
}

func (suite *LoggerTester) Test08Params() {
	t := suite.T()
	assert := assert.New(t)

	suite.setupFixture()
	defer suite.teardownFixture()

	uniq := "Test08/" + strconv.Itoa(time.Now().Nanosecond())

	delta := time.Duration(10 * time.Second)
	tstart := time.Now().Add(-delta).UTC()

	err := suite.logger.Information(uniq)
	assert.NoError(err)

	tend := time.Now().Add(delta).UTC()

	sleep(1)

	format := &piazza.JsonPagination{
		PerPage: 256,
		Page:    0,
		Order:   piazza.SortOrderDescending,
		SortBy:  "timeStamp",
	}

	// test date range params
	{

		params := &piazza.HttpQueryParams{}
		params.AddTime("after", tstart)
		params.AddTime("before", tend)

		msgs, cnt, err := suite.httpWriter.GetMessages(format, params)
		assert.NoError(err)

		assert.True(cnt >= 1)
		assert.True(len(msgs) >= 1)
	}

	// test service param
	{
		params := &piazza.HttpQueryParams{}
		params.AddString("service", suite.mssgApplication)

		msgs, _, err := suite.httpWriter.GetMessages(format, params)
		assert.NoError(err)

		assert.Len(msgs, 1)
	}

	// test contains param
	{
		params := &piazza.HttpQueryParams{}
		params.AddString("contains", suite.mssgHostName)

		msgs, _, err := suite.httpWriter.GetMessages(format, params)
		assert.NoError(err)

		assert.True(len(msgs) >= 1)
	}
}
<|endoftext|>


<commit_before>// Copyright The OpenTelemetry 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 grpctrace

// gRPC tracing middleware
// https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/rpc.md
import (
	"context"
	"io"
	"net"
	"strings"

	"go.opentelemetry.io/otel/api/standard"

	"github.com/golang/protobuf/proto" //nolint:staticcheck

	"google.golang.org/grpc"
	"google.golang.org/grpc/metadata"
	"google.golang.org/grpc/peer"
	"google.golang.org/grpc/status"

	"go.opentelemetry.io/otel/api/correlation"
	"go.opentelemetry.io/otel/api/kv"
	"go.opentelemetry.io/otel/api/trace"
)

type messageType kv.KeyValue

// Event adds an event of the messageType to the span associated with the
// passed context with id and size (if message is a proto message).
func (m messageType) Event(ctx context.Context, id int, message interface{}) {
	span := trace.SpanFromContext(ctx)
	if p, ok := message.(proto.Message); ok {
		span.AddEvent(ctx, "message",
			kv.KeyValue(m),
			standard.RPCMessageIDKey.Int(id),
			standard.RPCMessageUncompressedSizeKey.Int(proto.Size(p)),
		)
	} else {
		span.AddEvent(ctx, "message",
			kv.KeyValue(m),
			standard.RPCMessageIDKey.Int(id),
		)
	}
}

var (
	messageSent     = messageType(standard.RPCMessageTypeSent)
	messageReceived = messageType(standard.RPCMessageTypeReceived)
)

// UnaryClientInterceptor returns a grpc.UnaryClientInterceptor suitable
// for use in a grpc.Dial call.
//
// For example:
//     tracer := global.Tracer("client-tracer")
//     s := grpc.NewServer(
//         grpc.WithUnaryInterceptor(grpctrace.UnaryClientInterceptor(tracer)),
//         ...,  // (existing DialOptions))
func UnaryClientInterceptor(tracer trace.Tracer) grpc.UnaryClientInterceptor {
	return func(
		ctx context.Context,
		method string,
		req, reply interface{},
		cc *grpc.ClientConn,
		invoker grpc.UnaryInvoker,
		opts ...grpc.CallOption,
	) error {
		requestMetadata, _ := metadata.FromOutgoingContext(ctx)
		metadataCopy := requestMetadata.Copy()

		name, attr := spanInfo(method, cc.Target())
		var span trace.Span
		ctx, span = tracer.Start(
			ctx,
			name,
			trace.WithSpanKind(trace.SpanKindClient),
			trace.WithAttributes(attr...),
		)
		defer span.End()

		Inject(ctx, &metadataCopy)
		ctx = metadata.NewOutgoingContext(ctx, metadataCopy)

		messageSent.Event(ctx, 1, req)

		err := invoker(ctx, method, req, reply, cc, opts...)

		messageReceived.Event(ctx, 1, reply)

		if err != nil {
			s, _ := status.FromError(err)
			span.SetStatus(s.Code(), s.Message())
		}

		return err
	}
}

type streamEventType int

type streamEvent struct {
	Type streamEventType
	Err  error
}

const (
	closeEvent streamEventType = iota
	receiveEndEvent
	errorEvent
)

// clientStream  wraps around the embedded grpc.ClientStream, and intercepts the RecvMsg and
// SendMsg method call.
type clientStream struct {
	grpc.ClientStream

	desc       *grpc.StreamDesc
	events     chan streamEvent
	eventsDone chan struct{}
	finished   chan error

	receivedMessageID int
	sentMessageID     int
}

var _ = proto.Marshal

func (w *clientStream) RecvMsg(m interface{}) error {
	err := w.ClientStream.RecvMsg(m)

	if err == nil && !w.desc.ServerStreams {
		w.sendStreamEvent(receiveEndEvent, nil)
	} else if err == io.EOF {
		w.sendStreamEvent(receiveEndEvent, nil)
	} else if err != nil {
		w.sendStreamEvent(errorEvent, err)
	} else {
		w.receivedMessageID++
		messageReceived.Event(w.Context(), w.receivedMessageID, m)
	}

	return err
}

func (w *clientStream) SendMsg(m interface{}) error {
	err := w.ClientStream.SendMsg(m)

	w.sentMessageID++
	messageSent.Event(w.Context(), w.sentMessageID, m)

	if err != nil {
		w.sendStreamEvent(errorEvent, err)
	}

	return err
}

func (w *clientStream) Header() (metadata.MD, error) {
	md, err := w.ClientStream.Header()

	if err != nil {
		w.sendStreamEvent(errorEvent, err)
	}

	return md, err
}

func (w *clientStream) CloseSend() error {
	err := w.ClientStream.CloseSend()

	if err != nil {
		w.sendStreamEvent(errorEvent, err)
	} else {
		w.sendStreamEvent(closeEvent, nil)
	}

	return err
}

const (
	clientClosedState byte = 1 << iota
	receiveEndedState
)

func wrapClientStream(s grpc.ClientStream, desc *grpc.StreamDesc) *clientStream {
	events := make(chan streamEvent)
	eventsDone := make(chan struct{})
	finished := make(chan error)

	go func() {
		defer close(eventsDone)

		// Both streams have to be closed
		state := byte(0)

		for event := range events {
			switch event.Type {
			case closeEvent:
				state |= clientClosedState
			case receiveEndEvent:
				state |= receiveEndedState
			case errorEvent:
				finished <- event.Err
				return
			}

			if state == clientClosedState|receiveEndedState {
				finished <- nil
				return
			}
		}
	}()

	return &clientStream{
		ClientStream: s,
		desc:         desc,
		events:       events,
		eventsDone:   eventsDone,
		finished:     finished,
	}
}

func (w *clientStream) sendStreamEvent(eventType streamEventType, err error) {
	select {
	case <-w.eventsDone:
	case w.events <- streamEvent{Type: eventType, Err: err}:
	}
}

// StreamClientInterceptor returns a grpc.StreamClientInterceptor suitable
// for use in a grpc.Dial call.
//
// For example:
//     tracer := global.Tracer("client-tracer")
//     s := grpc.Dial(
//         grpc.WithStreamInterceptor(grpctrace.StreamClientInterceptor(tracer)),
//         ...,  // (existing DialOptions))
func StreamClientInterceptor(tracer trace.Tracer) grpc.StreamClientInterceptor {
	return func(
		ctx context.Context,
		desc *grpc.StreamDesc,
		cc *grpc.ClientConn,
		method string,
		streamer grpc.Streamer,
		opts ...grpc.CallOption,
	) (grpc.ClientStream, error) {
		requestMetadata, _ := metadata.FromOutgoingContext(ctx)
		metadataCopy := requestMetadata.Copy()

		name, attr := spanInfo(method, cc.Target())
		var span trace.Span
		ctx, span = tracer.Start(
			ctx,
			name,
			trace.WithSpanKind(trace.SpanKindClient),
			trace.WithAttributes(attr...),
		)

		Inject(ctx, &metadataCopy)
		ctx = metadata.NewOutgoingContext(ctx, metadataCopy)

		s, err := streamer(ctx, desc, cc, method, opts...)
		stream := wrapClientStream(s, desc)

		go func() {
			if err == nil {
				err = <-stream.finished
			}

			if err != nil {
				s, _ := status.FromError(err)
				span.SetStatus(s.Code(), s.Message())
			}

			span.End()
		}()

		return stream, err
	}
}

// UnaryServerInterceptor returns a grpc.UnaryServerInterceptor suitable
// for use in a grpc.NewServer call.
//
// For example:
//     tracer := global.Tracer("client-tracer")
//     s := grpc.Dial(
//         grpc.UnaryInterceptor(grpctrace.UnaryServerInterceptor(tracer)),
//         ...,  // (existing ServerOptions))
func UnaryServerInterceptor(tracer trace.Tracer) grpc.UnaryServerInterceptor {
	return func(
		ctx context.Context,
		req interface{},
		info *grpc.UnaryServerInfo,
		handler grpc.UnaryHandler,
	) (interface{}, error) {
		requestMetadata, _ := metadata.FromIncomingContext(ctx)
		metadataCopy := requestMetadata.Copy()

		entries, spanCtx := Extract(ctx, &metadataCopy)
		ctx = correlation.ContextWithMap(ctx, correlation.NewMap(correlation.MapUpdate{
			MultiKV: entries,
		}))

		name, attr := spanInfo(info.FullMethod, peerFromCtx(ctx))
		ctx, span := tracer.Start(
			trace.ContextWithRemoteSpanContext(ctx, spanCtx),
			name,
			trace.WithSpanKind(trace.SpanKindServer),
			trace.WithAttributes(attr...),
		)
		defer span.End()

		messageReceived.Event(ctx, 1, req)

		resp, err := handler(ctx, req)
		if err != nil {
			s, _ := status.FromError(err)
			span.SetStatus(s.Code(), s.Message())
			messageSent.Event(ctx, 1, s.Proto())
		} else {
			messageSent.Event(ctx, 1, resp)
		}

		return resp, err
	}
}

// clientStream wraps around the embedded grpc.ServerStream, and intercepts the RecvMsg and
// SendMsg method call.
type serverStream struct {
	grpc.ServerStream
	ctx context.Context

	receivedMessageID int
	sentMessageID     int
}

func (w *serverStream) Context() context.Context {
	return w.ctx
}

func (w *serverStream) RecvMsg(m interface{}) error {
	err := w.ServerStream.RecvMsg(m)

	if err == nil {
		w.receivedMessageID++
		messageReceived.Event(w.Context(), w.receivedMessageID, m)
	}

	return err
}

func (w *serverStream) SendMsg(m interface{}) error {
	err := w.ServerStream.SendMsg(m)

	w.sentMessageID++
	messageSent.Event(w.Context(), w.sentMessageID, m)

	return err
}

func wrapServerStream(ctx context.Context, ss grpc.ServerStream) *serverStream {
	return &serverStream{
		ServerStream: ss,
		ctx:          ctx,
	}
}

// StreamServerInterceptor returns a grpc.StreamServerInterceptor suitable
// for use in a grpc.NewServer call.
//
// For example:
//     tracer := global.Tracer("client-tracer")
//     s := grpc.Dial(
//         grpc.StreamInterceptor(grpctrace.StreamServerInterceptor(tracer)),
//         ...,  // (existing ServerOptions))
func StreamServerInterceptor(tracer trace.Tracer) grpc.StreamServerInterceptor {
	return func(
		srv interface{},
		ss grpc.ServerStream,
		info *grpc.StreamServerInfo,
		handler grpc.StreamHandler,
	) error {
		ctx := ss.Context()

		requestMetadata, _ := metadata.FromIncomingContext(ctx)
		metadataCopy := requestMetadata.Copy()

		entries, spanCtx := Extract(ctx, &metadataCopy)
		ctx = correlation.ContextWithMap(ctx, correlation.NewMap(correlation.MapUpdate{
			MultiKV: entries,
		}))

		name, attr := spanInfo(info.FullMethod, peerFromCtx(ctx))
		ctx, span := tracer.Start(
			trace.ContextWithRemoteSpanContext(ctx, spanCtx),
			name,
			trace.WithSpanKind(trace.SpanKindServer),
			trace.WithAttributes(attr...),
		)
		defer span.End()

		err := handler(srv, wrapServerStream(ctx, ss))

		if err != nil {
			s, _ := status.FromError(err)
			span.SetStatus(s.Code(), s.Message())
		}

		return err
	}
}

// spanInfo returns a span name and all appropriate attributes from the gRPC
// method and peer address.
func spanInfo(fullMethod, peerAddress string) (string, []kv.KeyValue) {
	attrs := []kv.KeyValue{standard.RPCSystemGRPC}
	name, mAttrs := parseFullMethod(fullMethod)
	attrs = append(attrs, mAttrs...)
	attrs = append(attrs, peerAttr(peerAddress)...)
	return name, attrs
}

// peerAttr returns attributes about the peer address.
func peerAttr(addr string) []kv.KeyValue {
	host, port, err := net.SplitHostPort(addr)
	if err != nil {
		return []kv.KeyValue(nil)
	}

	if host == "" {
		host = "127.0.0.1"
	}

	return []kv.KeyValue{
		standard.NetPeerIPKey.String(host),
		standard.NetPeerPortKey.String(port),
	}
}

// peerFromCtx returns a peer address from a context, if one exists.
func peerFromCtx(ctx context.Context) string {
	p, ok := peer.FromContext(ctx)
	if !ok {
		return ""
	}
	return p.Addr.String()
}

// parseFullMethod returns a span name following the OpenTelemetry semantic
// conventions as well as all applicable span kv.KeyValue attributes based
// on a gRPC's FullMethod.
func parseFullMethod(fullMethod string) (string, []kv.KeyValue) {
	name := strings.TrimLeft(fullMethod, "/")
	parts := strings.SplitN(name, "/", 2)
	if len(parts) != 2 {
		// Invalid format, does not follow `/package.service/method`.
		return name, []kv.KeyValue(nil)
	}

	var attrs []kv.KeyValue
	if service := parts[0]; service != "" {
		attrs = append(attrs, standard.RPCServiceKey.String(service))
	}
	if method := parts[1]; method != "" {
		attrs = append(attrs, standard.RPCMethodKey.String(method))
	}
	return name, attrs
}
<commit_msg>Fix typo in comment (#951)<commit_after>// Copyright The OpenTelemetry 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 grpctrace

// gRPC tracing middleware
// https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/rpc.md
import (
	"context"
	"io"
	"net"
	"strings"

	"go.opentelemetry.io/otel/api/standard"

	"github.com/golang/protobuf/proto" //nolint:staticcheck

	"google.golang.org/grpc"
	"google.golang.org/grpc/metadata"
	"google.golang.org/grpc/peer"
	"google.golang.org/grpc/status"

	"go.opentelemetry.io/otel/api/correlation"
	"go.opentelemetry.io/otel/api/kv"
	"go.opentelemetry.io/otel/api/trace"
)

type messageType kv.KeyValue

// Event adds an event of the messageType to the span associated with the
// passed context with id and size (if message is a proto message).
func (m messageType) Event(ctx context.Context, id int, message interface{}) {
	span := trace.SpanFromContext(ctx)
	if p, ok := message.(proto.Message); ok {
		span.AddEvent(ctx, "message",
			kv.KeyValue(m),
			standard.RPCMessageIDKey.Int(id),
			standard.RPCMessageUncompressedSizeKey.Int(proto.Size(p)),
		)
	} else {
		span.AddEvent(ctx, "message",
			kv.KeyValue(m),
			standard.RPCMessageIDKey.Int(id),
		)
	}
}

var (
	messageSent     = messageType(standard.RPCMessageTypeSent)
	messageReceived = messageType(standard.RPCMessageTypeReceived)
)

// UnaryClientInterceptor returns a grpc.UnaryClientInterceptor suitable
// for use in a grpc.Dial call.
//
// For example:
//     tracer := global.Tracer("client-tracer")
//     s := grpc.NewServer(
//         grpc.WithUnaryInterceptor(grpctrace.UnaryClientInterceptor(tracer)),
//         ...,  // (existing DialOptions))
func UnaryClientInterceptor(tracer trace.Tracer) grpc.UnaryClientInterceptor {
	return func(
		ctx context.Context,
		method string,
		req, reply interface{},
		cc *grpc.ClientConn,
		invoker grpc.UnaryInvoker,
		opts ...grpc.CallOption,
	) error {
		requestMetadata, _ := metadata.FromOutgoingContext(ctx)
		metadataCopy := requestMetadata.Copy()

		name, attr := spanInfo(method, cc.Target())
		var span trace.Span
		ctx, span = tracer.Start(
			ctx,
			name,
			trace.WithSpanKind(trace.SpanKindClient),
			trace.WithAttributes(attr...),
		)
		defer span.End()

		Inject(ctx, &metadataCopy)
		ctx = metadata.NewOutgoingContext(ctx, metadataCopy)

		messageSent.Event(ctx, 1, req)

		err := invoker(ctx, method, req, reply, cc, opts...)

		messageReceived.Event(ctx, 1, reply)

		if err != nil {
			s, _ := status.FromError(err)
			span.SetStatus(s.Code(), s.Message())
		}

		return err
	}
}

type streamEventType int

type streamEvent struct {
	Type streamEventType
	Err  error
}

const (
	closeEvent streamEventType = iota
	receiveEndEvent
	errorEvent
)

// clientStream  wraps around the embedded grpc.ClientStream, and intercepts the RecvMsg and
// SendMsg method call.
type clientStream struct {
	grpc.ClientStream

	desc       *grpc.StreamDesc
	events     chan streamEvent
	eventsDone chan struct{}
	finished   chan error

	receivedMessageID int
	sentMessageID     int
}

var _ = proto.Marshal

func (w *clientStream) RecvMsg(m interface{}) error {
	err := w.ClientStream.RecvMsg(m)

	if err == nil && !w.desc.ServerStreams {
		w.sendStreamEvent(receiveEndEvent, nil)
	} else if err == io.EOF {
		w.sendStreamEvent(receiveEndEvent, nil)
	} else if err != nil {
		w.sendStreamEvent(errorEvent, err)
	} else {
		w.receivedMessageID++
		messageReceived.Event(w.Context(), w.receivedMessageID, m)
	}

	return err
}

func (w *clientStream) SendMsg(m interface{}) error {
	err := w.ClientStream.SendMsg(m)

	w.sentMessageID++
	messageSent.Event(w.Context(), w.sentMessageID, m)

	if err != nil {
		w.sendStreamEvent(errorEvent, err)
	}

	return err
}

func (w *clientStream) Header() (metadata.MD, error) {
	md, err := w.ClientStream.Header()

	if err != nil {
		w.sendStreamEvent(errorEvent, err)
	}

	return md, err
}

func (w *clientStream) CloseSend() error {
	err := w.ClientStream.CloseSend()

	if err != nil {
		w.sendStreamEvent(errorEvent, err)
	} else {
		w.sendStreamEvent(closeEvent, nil)
	}

	return err
}

const (
	clientClosedState byte = 1 << iota
	receiveEndedState
)

func wrapClientStream(s grpc.ClientStream, desc *grpc.StreamDesc) *clientStream {
	events := make(chan streamEvent)
	eventsDone := make(chan struct{})
	finished := make(chan error)

	go func() {
		defer close(eventsDone)

		// Both streams have to be closed
		state := byte(0)

		for event := range events {
			switch event.Type {
			case closeEvent:
				state |= clientClosedState
			case receiveEndEvent:
				state |= receiveEndedState
			case errorEvent:
				finished <- event.Err
				return
			}

			if state == clientClosedState|receiveEndedState {
				finished <- nil
				return
			}
		}
	}()

	return &clientStream{
		ClientStream: s,
		desc:         desc,
		events:       events,
		eventsDone:   eventsDone,
		finished:     finished,
	}
}

func (w *clientStream) sendStreamEvent(eventType streamEventType, err error) {
	select {
	case <-w.eventsDone:
	case w.events <- streamEvent{Type: eventType, Err: err}:
	}
}

// StreamClientInterceptor returns a grpc.StreamClientInterceptor suitable
// for use in a grpc.Dial call.
//
// For example:
//     tracer := global.Tracer("client-tracer")
//     s := grpc.Dial(
//         grpc.WithStreamInterceptor(grpctrace.StreamClientInterceptor(tracer)),
//         ...,  // (existing DialOptions))
func StreamClientInterceptor(tracer trace.Tracer) grpc.StreamClientInterceptor {
	return func(
		ctx context.Context,
		desc *grpc.StreamDesc,
		cc *grpc.ClientConn,
		method string,
		streamer grpc.Streamer,
		opts ...grpc.CallOption,
	) (grpc.ClientStream, error) {
		requestMetadata, _ := metadata.FromOutgoingContext(ctx)
		metadataCopy := requestMetadata.Copy()

		name, attr := spanInfo(method, cc.Target())
		var span trace.Span
		ctx, span = tracer.Start(
			ctx,
			name,
			trace.WithSpanKind(trace.SpanKindClient),
			trace.WithAttributes(attr...),
		)

		Inject(ctx, &metadataCopy)
		ctx = metadata.NewOutgoingContext(ctx, metadataCopy)

		s, err := streamer(ctx, desc, cc, method, opts...)
		stream := wrapClientStream(s, desc)

		go func() {
			if err == nil {
				err = <-stream.finished
			}

			if err != nil {
				s, _ := status.FromError(err)
				span.SetStatus(s.Code(), s.Message())
			}

			span.End()
		}()

		return stream, err
	}
}

// UnaryServerInterceptor returns a grpc.UnaryServerInterceptor suitable
// for use in a grpc.NewServer call.
//
// For example:
//     tracer := global.Tracer("server-tracer")
//     s := grpc.Dial(
//         grpc.UnaryInterceptor(grpctrace.UnaryServerInterceptor(tracer)),
//         ...,  // (existing ServerOptions))
func UnaryServerInterceptor(tracer trace.Tracer) grpc.UnaryServerInterceptor {
	return func(
		ctx context.Context,
		req interface{},
		info *grpc.UnaryServerInfo,
		handler grpc.UnaryHandler,
	) (interface{}, error) {
		requestMetadata, _ := metadata.FromIncomingContext(ctx)
		metadataCopy := requestMetadata.Copy()

		entries, spanCtx := Extract(ctx, &metadataCopy)
		ctx = correlation.ContextWithMap(ctx, correlation.NewMap(correlation.MapUpdate{
			MultiKV: entries,
		}))

		name, attr := spanInfo(info.FullMethod, peerFromCtx(ctx))
		ctx, span := tracer.Start(
			trace.ContextWithRemoteSpanContext(ctx, spanCtx),
			name,
			trace.WithSpanKind(trace.SpanKindServer),
			trace.WithAttributes(attr...),
		)
		defer span.End()

		messageReceived.Event(ctx, 1, req)

		resp, err := handler(ctx, req)
		if err != nil {
			s, _ := status.FromError(err)
			span.SetStatus(s.Code(), s.Message())
			messageSent.Event(ctx, 1, s.Proto())
		} else {
			messageSent.Event(ctx, 1, resp)
		}

		return resp, err
	}
}

// serverStream wraps around the embedded grpc.ServerStream, and intercepts the RecvMsg and
// SendMsg method call.
type serverStream struct {
	grpc.ServerStream
	ctx context.Context

	receivedMessageID int
	sentMessageID     int
}

func (w *serverStream) Context() context.Context {
	return w.ctx
}

func (w *serverStream) RecvMsg(m interface{}) error {
	err := w.ServerStream.RecvMsg(m)

	if err == nil {
		w.receivedMessageID++
		messageReceived.Event(w.Context(), w.receivedMessageID, m)
	}

	return err
}

func (w *serverStream) SendMsg(m interface{}) error {
	err := w.ServerStream.SendMsg(m)

	w.sentMessageID++
	messageSent.Event(w.Context(), w.sentMessageID, m)

	return err
}

func wrapServerStream(ctx context.Context, ss grpc.ServerStream) *serverStream {
	return &serverStream{
		ServerStream: ss,
		ctx:          ctx,
	}
}

// StreamServerInterceptor returns a grpc.StreamServerInterceptor suitable
// for use in a grpc.NewServer call.
//
// For example:
//     tracer := global.Tracer("server-tracer")
//     s := grpc.Dial(
//         grpc.StreamInterceptor(grpctrace.StreamServerInterceptor(tracer)),
//         ...,  // (existing ServerOptions))
func StreamServerInterceptor(tracer trace.Tracer) grpc.StreamServerInterceptor {
	return func(
		srv interface{},
		ss grpc.ServerStream,
		info *grpc.StreamServerInfo,
		handler grpc.StreamHandler,
	) error {
		ctx := ss.Context()

		requestMetadata, _ := metadata.FromIncomingContext(ctx)
		metadataCopy := requestMetadata.Copy()

		entries, spanCtx := Extract(ctx, &metadataCopy)
		ctx = correlation.ContextWithMap(ctx, correlation.NewMap(correlation.MapUpdate{
			MultiKV: entries,
		}))

		name, attr := spanInfo(info.FullMethod, peerFromCtx(ctx))
		ctx, span := tracer.Start(
			trace.ContextWithRemoteSpanContext(ctx, spanCtx),
			name,
			trace.WithSpanKind(trace.SpanKindServer),
			trace.WithAttributes(attr...),
		)
		defer span.End()

		err := handler(srv, wrapServerStream(ctx, ss))

		if err != nil {
			s, _ := status.FromError(err)
			span.SetStatus(s.Code(), s.Message())
		}

		return err
	}
}

// spanInfo returns a span name and all appropriate attributes from the gRPC
// method and peer address.
func spanInfo(fullMethod, peerAddress string) (string, []kv.KeyValue) {
	attrs := []kv.KeyValue{standard.RPCSystemGRPC}
	name, mAttrs := parseFullMethod(fullMethod)
	attrs = append(attrs, mAttrs...)
	attrs = append(attrs, peerAttr(peerAddress)...)
	return name, attrs
}

// peerAttr returns attributes about the peer address.
func peerAttr(addr string) []kv.KeyValue {
	host, port, err := net.SplitHostPort(addr)
	if err != nil {
		return []kv.KeyValue(nil)
	}

	if host == "" {
		host = "127.0.0.1"
	}

	return []kv.KeyValue{
		standard.NetPeerIPKey.String(host),
		standard.NetPeerPortKey.String(port),
	}
}

// peerFromCtx returns a peer address from a context, if one exists.
func peerFromCtx(ctx context.Context) string {
	p, ok := peer.FromContext(ctx)
	if !ok {
		return ""
	}
	return p.Addr.String()
}

// parseFullMethod returns a span name following the OpenTelemetry semantic
// conventions as well as all applicable span kv.KeyValue attributes based
// on a gRPC's FullMethod.
func parseFullMethod(fullMethod string) (string, []kv.KeyValue) {
	name := strings.TrimLeft(fullMethod, "/")
	parts := strings.SplitN(name, "/", 2)
	if len(parts) != 2 {
		// Invalid format, does not follow `/package.service/method`.
		return name, []kv.KeyValue(nil)
	}

	var attrs []kv.KeyValue
	if service := parts[0]; service != "" {
		attrs = append(attrs, standard.RPCServiceKey.String(service))
	}
	if method := parts[1]; method != "" {
		attrs = append(attrs, standard.RPCMethodKey.String(method))
	}
	return name, attrs
}
<|endoftext|>


<commit_before>package main

import (
	"net"
	"os/exec"
	"strings"
	"testing"
)

func TestCliProxyDisableProxyUnixSock(t *testing.T) {
	cmd := exec.Command(dockerBinary, "info")
	cmd.Env = appendDockerHostEnv([]string{"HTTP_PROXY=http://127.0.0.1:9999"})

	if out, _, err := runCommandWithOutput(cmd); err != nil {
		t.Fatal(err, out)
	}

	logDone("cli proxy - HTTP_PROXY is not used when connecting to unix sock")
}

// Can't use localhost here since go has a special case to not use proxy if connecting to localhost
// See http://golang.org/pkg/net/http/#ProxyFromEnvironment
func TestCliProxyProxyTCPSock(t *testing.T) {
	testRequires(t, SameHostDaemon)
	// get the IP to use to connect since we can't use localhost
	addrs, err := net.InterfaceAddrs()
	if err != nil {
		t.Fatal(err)
	}
	var ip string
	for _, addr := range addrs {
		sAddr := addr.String()
		if !strings.Contains(sAddr, "127.0.0.1") {
			addrArr := strings.Split(sAddr, "/")
			ip = addrArr[0]
			break
		}
	}

	if ip == "" {
		t.Fatal("could not find ip to connect to")
	}

	d := NewDaemon(t)
	if err := d.Start("-H", "tcp://"+ip+":2375"); err != nil {
		t.Fatal(err)
	}

	cmd := exec.Command(dockerBinary, "info")
	cmd.Env = []string{"DOCKER_HOST=tcp://" + ip + ":2375", "HTTP_PROXY=127.0.0.1:9999"}
	if out, _, err := runCommandWithOutput(cmd); err == nil {
		t.Fatal(err, out)
	}

	// Test with no_proxy
	cmd.Env = append(cmd.Env, "NO_PROXY="+ip)
	if out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "info")); err != nil {
		t.Fatal(err, out)
	}

	logDone("cli proxy - HTTP_PROXY is used for TCP sock")
}
<commit_msg>integ-cli: skip test assuming -H is unix://...<commit_after>package main

import (
	"net"
	"os/exec"
	"strings"
	"testing"
)

func TestCliProxyDisableProxyUnixSock(t *testing.T) {
	testRequires(t, SameHostDaemon) // test is valid when DOCKER_HOST=unix://..

	cmd := exec.Command(dockerBinary, "info")
	cmd.Env = appendDockerHostEnv([]string{"HTTP_PROXY=http://127.0.0.1:9999"})

	if out, _, err := runCommandWithOutput(cmd); err != nil {
		t.Fatal(err, out)
	}

	logDone("cli proxy - HTTP_PROXY is not used when connecting to unix sock")
}

// Can't use localhost here since go has a special case to not use proxy if connecting to localhost
// See http://golang.org/pkg/net/http/#ProxyFromEnvironment
func TestCliProxyProxyTCPSock(t *testing.T) {
	testRequires(t, SameHostDaemon)
	// get the IP to use to connect since we can't use localhost
	addrs, err := net.InterfaceAddrs()
	if err != nil {
		t.Fatal(err)
	}
	var ip string
	for _, addr := range addrs {
		sAddr := addr.String()
		if !strings.Contains(sAddr, "127.0.0.1") {
			addrArr := strings.Split(sAddr, "/")
			ip = addrArr[0]
			break
		}
	}

	if ip == "" {
		t.Fatal("could not find ip to connect to")
	}

	d := NewDaemon(t)
	if err := d.Start("-H", "tcp://"+ip+":2375"); err != nil {
		t.Fatal(err)
	}

	cmd := exec.Command(dockerBinary, "info")
	cmd.Env = []string{"DOCKER_HOST=tcp://" + ip + ":2375", "HTTP_PROXY=127.0.0.1:9999"}
	if out, _, err := runCommandWithOutput(cmd); err == nil {
		t.Fatal(err, out)
	}

	// Test with no_proxy
	cmd.Env = append(cmd.Env, "NO_PROXY="+ip)
	if out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "info")); err != nil {
		t.Fatal(err, out)
	}

	logDone("cli proxy - HTTP_PROXY is used for TCP sock")
}
<|endoftext|>


<commit_before>package integration_test

import (
	"github.com/APTrust/exchange/context"
	"github.com/APTrust/exchange/models"
	"github.com/APTrust/exchange/util/testutil"
	"github.com/stretchr/testify/require"
	"path/filepath"
	"strings"
	"testing"
)

// apt_mark_for_restore_test is used by scripts/integration_test.rb
// to mark some APTrust bags for restoration, so that the apt_restore
// integration test will have some bags to work with.
func TestMarkForRestore(t *testing.T) {
	if !testutil.ShouldRunIntegrationTests() {
		t.Skip("Skipping integration test. Set ENV var RUN_EXCHANGE_INTEGRATION=true if you want to run them.")
	}
	configFile := filepath.Join("config", "integration.json")
	config, err := models.LoadConfigFile(configFile)
	require.Nil(t, err)
	_context := context.NewContext(config)
	for _, s3Key := range testutil.INTEGRATION_GOOD_BAGS[0:8] {
		identifier := strings.Replace(s3Key, "aptrust.integration.test", "test.edu", 1)
		identifier = strings.Replace(identifier, ".tar", "", 1)
		resp := _context.PharosClient.IntellectualObjectRequestRestore(identifier)
		workItem := resp.WorkItem()
		require.Nil(t, resp.Error)
		require.NotNil(t, workItem)
		_context.MessageLog.Info("Created restore request WorkItem #%d for %s",
			workItem.Id, workItem.ObjectIdentifier)
	}
}
<commit_msg>Queue file restorations<commit_after>package integration_test

import (
	"github.com/APTrust/exchange/context"
	"github.com/APTrust/exchange/models"
	"github.com/APTrust/exchange/util/testutil"
	"github.com/stretchr/testify/require"
	"path/filepath"
	"strings"
	"testing"
)

// apt_mark_for_restore_test is used by scripts/integration_test.rb
// to mark some APTrust bags for restoration, so that the apt_restore
// integration test will have some bags to work with.
func TestMarkForRestore(t *testing.T) {
	if !testutil.ShouldRunIntegrationTests() {
		t.Skip("Skipping integration test. Set ENV var RUN_EXCHANGE_INTEGRATION=true if you want to run them.")
	}
	configFile := filepath.Join("config", "integration.json")
	config, err := models.LoadConfigFile(configFile)
	require.Nil(t, err)
	_context := context.NewContext(config)

	// Request a few objects for restore.
	for _, s3Key := range testutil.INTEGRATION_GOOD_BAGS[0:8] {
		identifier := strings.Replace(s3Key, "aptrust.integration.test", "test.edu", 1)
		identifier = strings.Replace(identifier, ".tar", "", 1)
		resp := _context.PharosClient.IntellectualObjectRequestRestore(identifier)
		workItem := resp.WorkItem()
		require.Nil(t, resp.Error)
		require.NotNil(t, workItem)
		_context.MessageLog.Info("Created restore request WorkItem #%d for object %s",
			workItem.Id, workItem.ObjectIdentifier)
	}

	// And request a few files too.
	files := []string{
		"test.edu/example.edu.tagsample_good/data/datastream-DC",
		"test.edu/example.edu.tagsample_good/data/datastream-MARC",
	}
	for _, gfIdentifier := range files {
		resp := _context.PharosClient.GenericFileRequestRestore(gfIdentifier)
		workItem := resp.WorkItem()
		require.Nil(t, resp.Error)
		require.NotNil(t, workItem)
		_context.MessageLog.Info("Created restore request WorkItem #%d for file %s",
			workItem.Id, gfIdentifier)
	}
}
<|endoftext|>


<commit_before>package gosxnotifier

import (
	"archive/zip"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"runtime"
)

const (
	zipPath        = "terminal-notifier.temp.zip"
	executablePath = "terminal-notifier.app/Contents/MacOS/terminal-notifier"
	tempDirSuffix  = "gosxnotifier"
)

var (
	rootPath  string
	FinalPath string
)

func supportedOS() bool {
	if runtime.GOOS == "darwin" {
		return true
	} else {
		log.Print("OS does not support terminal-notifier")
		return false
	}
}

func init() {
	if supportedOS() {
		err := installTerminalNotifier()
		if err != nil {
			log.Fatalf("Could not install Terminal Notifier to a temp directory: %s", err)
		} else {
			FinalPath = filepath.Join(rootPath, executablePath)
		}
	}
}

func exists(file string) bool {
	if _, err := os.Stat(file); os.IsNotExist(err) {
		return false
	}
	return true
}

func installTerminalNotifier() error {
	rootPath = filepath.Join(os.TempDir(), tempDirSuffix)

	//if terminal-notifier.app already installed no-need to re-install
	if exists(filepath.Join(rootPath, executablePath)) {
		return nil
	}

	err := ioutil.WriteFile(zipPath, terminalnotifier(), 0700)
	if err != nil {
		return fmt.Errorf("could not write terminal-notifier file (%s): %s", zipPath, err)
	}

	defer os.Remove(zipPath)

	err = unpackZipArchive(zipPath, rootPath)
	if err != nil {
		return fmt.Errorf("could not unpack zip terminal-notifier file: %s", err)
	}

	err = os.Chmod(filepath.Join(rootPath, executablePath), 0755)
	if err != nil {
		return fmt.Errorf("could not make terminal-notifier executable: %s", err)
	}

	return nil
}

func unpackZipArchive(filename, tempPath string) error {
	reader, err := zip.OpenReader(filename)
	if err != nil {
		return err
	}

	defer reader.Close()

	for _, zipFile := range reader.Reader.File {
		name := zipFile.Name
		mode := zipFile.Mode()
		if mode.IsDir() {
			if err = os.MkdirAll(filepath.Join(tempPath, name), 0755); err != nil {
				return err
			}
		} else {
			if err = unpackZippedFile(name, tempPath, zipFile); err != nil {
				return err
			}
		}
	}

	return nil
}

func unpackZippedFile(filename, tempPath string, zipFile *zip.File) error {
	writer, err := os.Create(filepath.Join(tempPath, filename))

	if err != nil {
		return err
	}

	defer writer.Close()

	reader, err := zipFile.Open()
	if err != nil {
		return err
	}

	defer reader.Close()

	if _, err = io.Copy(writer, reader); err != nil {
		return err
	}

	return nil
}
<commit_msg>terminal-notifier: fix installation<commit_after>package gosxnotifier

import (
	"archive/zip"
	"bytes"
	"fmt"
	"io"
	"log"
	"os"
	"path/filepath"
	"runtime"
)

const (
	zipPath        = "terminal-notifier.temp.zip"
	executablePath = "terminal-notifier.app/Contents/MacOS/terminal-notifier"
	tempDirSuffix  = "gosxnotifier"
)

var (
	rootPath  string
	FinalPath string
)

func supportedOS() bool {
	if runtime.GOOS == "darwin" {
		return true
	} else {
		log.Print("OS does not support terminal-notifier")
		return false
	}
}

func init() {
	if supportedOS() {
		err := installTerminalNotifier()
		if err != nil {
			log.Fatalf("Could not install Terminal Notifier to a temp directory: %s", err)
		} else {
			FinalPath = filepath.Join(rootPath, executablePath)
		}
	}
}

func exists(file string) bool {
	if _, err := os.Stat(file); os.IsNotExist(err) {
		return false
	}
	return true
}

func installTerminalNotifier() error {
	rootPath = filepath.Join(os.TempDir(), tempDirSuffix)

	//if terminal-notifier.app already installed no-need to re-install
	if exists(filepath.Join(rootPath, executablePath)) {
		return nil
	}
	buf := bytes.NewReader(terminalnotifier())
	reader, err := zip.NewReader(buf, int64(buf.Len()))
	if err != nil {
		return err
	}
	err = unpackZip(reader, rootPath)
	if err != nil {
		return fmt.Errorf("could not unpack zip terminal-notifier file: %s", err)
	}

	err = os.Chmod(filepath.Join(rootPath, executablePath), 0755)
	if err != nil {
		return fmt.Errorf("could not make terminal-notifier executable: %s", err)
	}

	return nil
}

func unpackZip(reader *zip.Reader, tempPath string) error {
	for _, zipFile := range reader.File {
		name := zipFile.Name
		mode := zipFile.Mode()
		if mode.IsDir() {
			if err := os.MkdirAll(filepath.Join(tempPath, name), 0755); err != nil {
				return err
			}
		} else {
			if err := unpackZippedFile(name, tempPath, zipFile); err != nil {
				return err
			}
		}
	}

	return nil
}

func unpackZippedFile(filename, tempPath string, zipFile *zip.File) error {
	writer, err := os.Create(filepath.Join(tempPath, filename))

	if err != nil {
		return err
	}

	defer writer.Close()

	reader, err := zipFile.Open()
	if err != nil {
		return err
	}

	defer reader.Close()

	if _, err = io.Copy(writer, reader); err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package test

import (
	"testing"

	"github.com/heqzha/dcache/utils"
)

func TestDCache(t *testing.T) {
	pool := utils.GetCliPoolInst()
	cli, err := pool.Get("127.0.0.1:11000")
	if err != nil {
		t.Error(err)
		return
	}
	getRes, err := cli.Get("default", "test1")
	if err != nil {
		t.Error(err)
		return
	}
	t.Log(string(getRes.GetValue()))
	if _, err := cli.Set("default", "test1", []byte("Hello World")); err != nil {
		t.Error(err)
		return
	}
	getRes, err = cli.Get("default", "test1")
	if err != nil {
		t.Error(err)
		return
	}
	t.Log(string(getRes.GetValue()))

	delRes, err := cli.Del("default", "test1")
	if err != nil {
		t.Error(err)
		return
	}
	t.Log(string(delRes.GetValue()))
}
<commit_msg>update TestDCache<commit_after>package test

import (
	"testing"

	"github.com/heqzha/dcache/utils"
)

func TestDCache(t *testing.T) {
	pool := utils.GetCliPoolInst()
	cli, err := pool.Get("127.0.0.1:11000")
	if err != nil {
		t.Error(err)
		return
	}
	key := "test12"
	getRes, err := cli.Get("default", key)
	if err != nil {
		t.Error(err)
		return
	}
	t.Log(string(getRes.GetValue()))
	if _, err := cli.Set("default", key, []byte("Hello World")); err != nil {
		t.Error(err)
		return
	}
	getRes, err = cli.Get("default", key)
	if err != nil {
		t.Error(err)
		return
	}
	t.Log(string(getRes.GetValue()))

	delRes, err := cli.Del("default", key)
	if err != nil {
		t.Error(err)
		return
	}
	t.Log(string(delRes.GetValue()))
}
<|endoftext|>


<commit_before>package grpc

import (
	"google.golang.org/grpc"

	// TODO: Change when GRPC supports std library context
	pbempty "github.com/golang/protobuf/ptypes/empty"
	"golang.org/x/net/context"

	pbns "github.com/slok/ragnarok/grpc/nodestatus"
)

// TestClient is a GRPC client ready to test GRPC services
type TestClient struct {
	conn *grpc.ClientConn

	nsCli pbns.NodeStatusClient
}

// NewTestClient creates and returns a new test client
func NewTestClient(addr string) (*TestClient, error) {
	conn, err := grpc.Dial(addr, grpc.WithInsecure())
	if err != nil {
		return nil, err
	}

	return &TestClient{
		conn:  conn,
		nsCli: pbns.NewNodeStatusClient(conn),
	}, nil
}

// Close closes the GRPC connection.
func (t *TestClient) Close() error {
	return t.conn.Close()
}

// NodeStatusRegister wraps the call to nodestatus service.
func (t *TestClient) NodeStatusRegister(ctx context.Context, ni *pbns.Node) (*pbns.RegisteredResponse, error) {
	return t.nsCli.Register(ctx, ni)
}

// NodeStatusHeartbeat wraps the call to nodestatus service.
func (t *TestClient) NodeStatusHeartbeat(ctx context.Context, ns *pbns.NodeState) (*pbempty.Empty, error) {
	return t.nsCli.Heartbeat(ctx, ns)
}
<commit_msg>Add failure status grpc testing client<commit_after>package grpc

import (
	"google.golang.org/grpc"

	// TODO: Change when GRPC supports std library context
	pbempty "github.com/golang/protobuf/ptypes/empty"
	"golang.org/x/net/context"

	pbfs "github.com/slok/ragnarok/grpc/failurestatus"
	pbns "github.com/slok/ragnarok/grpc/nodestatus"
)

// TestClient is a GRPC client ready to test GRPC services
type TestClient struct {
	conn *grpc.ClientConn

	nsCli pbns.NodeStatusClient
	fsCli pbfs.FailureStatusClient
}

// NewTestClient creates and returns a new test client
func NewTestClient(addr string) (*TestClient, error) {
	conn, err := grpc.Dial(addr, grpc.WithInsecure())
	if err != nil {
		return nil, err
	}

	return &TestClient{
		conn:  conn,
		nsCli: pbns.NewNodeStatusClient(conn),
	}, nil
}

// Close closes the GRPC connection.
func (t *TestClient) Close() error {
	return t.conn.Close()
}

// NodeStatusRegister wraps the call to nodestatus service.
func (t *TestClient) NodeStatusRegister(ctx context.Context, ni *pbns.Node) (*pbns.RegisteredResponse, error) {
	return t.nsCli.Register(ctx, ni)
}

// NodeStatusHeartbeat wraps the call to nodestatus service.
func (t *TestClient) NodeStatusHeartbeat(ctx context.Context, ns *pbns.NodeState) (*pbempty.Empty, error) {
	return t.nsCli.Heartbeat(ctx, ns)
}

// FailureStatusGetFailure wraps the call to failurestatus service.
func (t *TestClient) FailureStatusGetFailure(ctx context.Context, fID *pbfs.FailureId) (*pbfs.Failure, error) {
	return t.fsCli.GetFailure(ctx, fID)
}

// FailureStatusFailureStateList wraps the call to failurestatus service.
func (t *TestClient) FailureStatusFailureStateList(ctx context.Context, nID *pbfs.NodeId) (pbfs.FailureStatus_FailureStateListClient, error) {
	return t.fsCli.FailureStateList(ctx, nID)
}
<|endoftext|>


<commit_before>package bounded_tree

import (
	"sync"

	"github.com/chrislusf/seaweedfs/weed/glog"
	"github.com/chrislusf/seaweedfs/weed/util"
)

type Node struct {
	Parent   *Node
	Name     string
	Children map[string]*Node
}

type BoundedTree struct {
	root *Node
	sync.RWMutex
	baseDir util.FullPath
}

func NewBoundedTree(baseDir util.FullPath) *BoundedTree {
	return &BoundedTree{
		root: &Node{
			Name: "/",
		},
		baseDir: baseDir,
	}
}

type VisitNodeFunc func(path util.FullPath) (childDirectories []string, err error)

// If the path is not visited, call the visitFn for each level of directory
// No action if the directory has been visited before or does not exist.
// A leaf node, which has no children, represents a directory not visited.
// A non-leaf node or a non-existing node represents a directory already visited, or does not need to visit.
func (t *BoundedTree) EnsureVisited(p util.FullPath, visitFn VisitNodeFunc) (visitErr error) {
	t.Lock()
	defer t.Unlock()

	if t.root == nil {
		return
	}
	components := p.Split()
	// fmt.Printf("components %v %d\n", components, len(components))
	canDelete, err := t.ensureVisited(t.root, t.baseDir, components, 0, visitFn)
	if err != nil {
		return err
	}
	if canDelete {
		t.root = nil
	}
	return nil
}

func (t *BoundedTree) ensureVisited(n *Node, currentPath util.FullPath, components []string, i int, visitFn VisitNodeFunc) (canDeleteNode bool, visitErr error) {

	// println("ensureVisited", currentPath, i)

	if n == nil {
		// fmt.Printf("%s null\n", currentPath)
		return
	}

	if n.isVisited() {
		// fmt.Printf("%s visited %v\n", currentPath, n.Name)
	} else {
		// fmt.Printf("ensure %v\n", currentPath)

		children, err := visitFn(currentPath)
		if err != nil {
			glog.V(0).Infof("failed to visit %s: %v", currentPath, err)
			return false, err
		}

		if len(children) == 0 {
			// fmt.Printf("  canDelete %v without children\n", currentPath)
			return true, nil
		}

		n.Children = make(map[string]*Node)
		for _, child := range children {
			// fmt.Printf("  add child %v %v\n", currentPath, child)
			n.Children[child] = &Node{
				Name: child,
			}
		}
	}

	if i >= len(components) {
		return
	}

	// fmt.Printf("  check child %v %v\n", currentPath, components[i])

	toVisitNode, found := n.Children[components[i]]
	if !found {
		// fmt.Printf("  did not find child %v %v\n", currentPath, components[i])
		return
	}

	// fmt.Printf("  ensureVisited %v %v\n", currentPath, toVisitNode.Name)
	canDelete, childVisitErr := t.ensureVisited(toVisitNode, currentPath.Child(components[i]), components, i+1, visitFn)
	if childVisitErr != nil {
		return false, childVisitErr
	}
	if canDelete {

		// fmt.Printf("  delete %v %v\n", currentPath, components[i])
		delete(n.Children, components[i])

		if len(n.Children) == 0 {
			// fmt.Printf("  canDelete %v\n", currentPath)
			return true, nil
		}
	}

	return false, nil

}

func (n *Node) isVisited() bool {
	if n == nil {
		return true
	}
	if len(n.Children) > 0 {
		return true
	}
	return false
}

func (n *Node) getChild(childName string) *Node {
	if n == nil {
		return nil
	}
	if len(n.Children) > 0 {
		return n.Children[childName]
	}
	return nil
}

func (t *BoundedTree) HasVisited(p util.FullPath) bool {

	t.RLock()
	defer t.RUnlock()

	if t.root == nil {
		return true
	}

	components := p.Split()
	// fmt.Printf("components %v %d\n", components, len(components))
	return t.hasVisited(t.root, util.FullPath("/"), components, 0)
}

func (t *BoundedTree) hasVisited(n *Node, currentPath util.FullPath, components []string, i int) bool {

	if n == nil {
		return true
	}

	if !n.isVisited() {
		return false
	}

	// fmt.Printf("  hasVisited child %v %+v %d\n", currentPath, components, i)

	if i >= len(components) {
		return true
	}

	toVisitNode, found := n.Children[components[i]]
	if !found {
		return true
	}

	return t.hasVisited(toVisitNode, currentPath.Child(components[i]), components, i+1)

}
<commit_msg>fuse: important: if filer -filer.path is not root, directory listing will fail<commit_after>package bounded_tree

import (
	"sync"

	"github.com/chrislusf/seaweedfs/weed/glog"
	"github.com/chrislusf/seaweedfs/weed/util"
)

type Node struct {
	Parent   *Node
	Name     string
	Children map[string]*Node
}

type BoundedTree struct {
	root *Node
	sync.RWMutex
	baseDir util.FullPath
}

func NewBoundedTree(baseDir util.FullPath) *BoundedTree {
	return &BoundedTree{
		root: &Node{
			Name: "/",
		},
		baseDir: baseDir,
	}
}

type VisitNodeFunc func(path util.FullPath) (childDirectories []string, err error)

// If the path is not visited, call the visitFn for each level of directory
// No action if the directory has been visited before or does not exist.
// A leaf node, which has no children, represents a directory not visited.
// A non-leaf node or a non-existing node represents a directory already visited, or does not need to visit.
func (t *BoundedTree) EnsureVisited(p util.FullPath, visitFn VisitNodeFunc) (visitErr error) {
	t.Lock()
	defer t.Unlock()

	if t.root == nil {
		return
	}
	components := util.FullPath(p[len(t.baseDir):]).Split()
	// fmt.Printf("components %v %d\n", components, len(components))
	canDelete, err := t.ensureVisited(t.root, t.baseDir, components, 0, visitFn)
	if err != nil {
		return err
	}
	if canDelete {
		t.root = nil
	}
	return nil
}

func (t *BoundedTree) ensureVisited(n *Node, currentPath util.FullPath, components []string, i int, visitFn VisitNodeFunc) (canDeleteNode bool, visitErr error) {

	// println("ensureVisited", currentPath, i)

	if n == nil {
		// fmt.Printf("%s null\n", currentPath)
		return
	}

	if n.isVisited() {
		// fmt.Printf("%s visited %v\n", currentPath, n.Name)
	} else {
		// fmt.Printf("ensure %v\n", currentPath)

		children, err := visitFn(currentPath)
		if err != nil {
			glog.V(0).Infof("failed to visit %s: %v", currentPath, err)
			return false, err
		}

		if len(children) == 0 {
			// fmt.Printf("  canDelete %v without children\n", currentPath)
			return true, nil
		}

		n.Children = make(map[string]*Node)
		for _, child := range children {
			// fmt.Printf("  add child %v %v\n", currentPath, child)
			n.Children[child] = &Node{
				Name: child,
			}
		}
	}

	if i >= len(components) {
		return
	}

	// fmt.Printf("  check child %v %v\n", currentPath, components[i])

	toVisitNode, found := n.Children[components[i]]
	if !found {
		// fmt.Printf("  did not find child %v %v\n", currentPath, components[i])
		return
	}

	// fmt.Printf("  ensureVisited %v %v\n", currentPath, toVisitNode.Name)
	canDelete, childVisitErr := t.ensureVisited(toVisitNode, currentPath.Child(components[i]), components, i+1, visitFn)
	if childVisitErr != nil {
		return false, childVisitErr
	}
	if canDelete {

		// fmt.Printf("  delete %v %v\n", currentPath, components[i])
		delete(n.Children, components[i])

		if len(n.Children) == 0 {
			// fmt.Printf("  canDelete %v\n", currentPath)
			return true, nil
		}
	}

	return false, nil

}

func (n *Node) isVisited() bool {
	if n == nil {
		return true
	}
	if len(n.Children) > 0 {
		return true
	}
	return false
}

func (n *Node) getChild(childName string) *Node {
	if n == nil {
		return nil
	}
	if len(n.Children) > 0 {
		return n.Children[childName]
	}
	return nil
}

func (t *BoundedTree) HasVisited(p util.FullPath) bool {

	t.RLock()
	defer t.RUnlock()

	if t.root == nil {
		return true
	}

	components := p.Split()
	// fmt.Printf("components %v %d\n", components, len(components))
	return t.hasVisited(t.root, util.FullPath("/"), components, 0)
}

func (t *BoundedTree) hasVisited(n *Node, currentPath util.FullPath, components []string, i int) bool {

	if n == nil {
		return true
	}

	if !n.isVisited() {
		return false
	}

	// fmt.Printf("  hasVisited child %v %+v %d\n", currentPath, components, i)

	if i >= len(components) {
		return true
	}

	toVisitNode, found := n.Children[components[i]]
	if !found {
		return true
	}

	return t.hasVisited(toVisitNode, currentPath.Child(components[i]), components, i+1)

}
<|endoftext|>


<commit_before>package iso

import (
	"fmt"

	parallelscommon "github.com/hashicorp/packer/builder/parallels/common"
	"github.com/hashicorp/packer/packer"
	"github.com/mitchellh/multistep"
)

// This step creates the actual virtual machine.
//
// Produces:
//   vmName string - The name of the VM
type stepCreateVM struct {
	vmName string
}

func (s *stepCreateVM) Run(state multistep.StateBag) multistep.StepAction {

	config := state.Get("config").(*Config)
	driver := state.Get("driver").(parallelscommon.Driver)
	ui := state.Get("ui").(packer.Ui)
	name := config.VMName

	command := []string{
		"create", name,
		"--distribution", config.GuestOSType,
		"--dst", config.OutputDir,
		"--vmtype", "vm",
		"--no-hdd",
	}

	ui.Say("Creating virtual machine...")
	if err := driver.Prlctl(command...); err != nil {
		err := fmt.Errorf("Error creating VM: %s", err)
		state.Put("error", err)
		ui.Error(err.Error())
		return multistep.ActionHalt
	}

	ui.Say("Applying default settings...")
	if err := driver.SetDefaultConfiguration(name); err != nil {
		err := fmt.Errorf("Error VM configuration: %s", err)
		state.Put("error", err)
		ui.Error(err.Error())
		return multistep.ActionHalt
	}

	// Set the VM name property on the first command
	if s.vmName == "" {
		s.vmName = name
	}

	// Set the final name in the state bag so others can use it
	state.Put("vmName", s.vmName)
	return multistep.ActionContinue
}

func (s *stepCreateVM) Cleanup(state multistep.StateBag) {
	if s.vmName == "" {
		return
	}

	driver := state.Get("driver").(parallelscommon.Driver)
	ui := state.Get("ui").(packer.Ui)

	ui.Say("Unregistering virtual machine...")
	if err := driver.Prlctl("unregister", s.vmName); err != nil {
		ui.Error(fmt.Sprintf("Error unregistering virtual machine: %s", err))
	}
}
<commit_msg>parallels: Remove soon to be removed --vmtype flag<commit_after>package iso

import (
	"fmt"

	parallelscommon "github.com/hashicorp/packer/builder/parallels/common"
	"github.com/hashicorp/packer/packer"
	"github.com/mitchellh/multistep"
)

// This step creates the actual virtual machine.
//
// Produces:
//   vmName string - The name of the VM
type stepCreateVM struct {
	vmName string
}

func (s *stepCreateVM) Run(state multistep.StateBag) multistep.StepAction {

	config := state.Get("config").(*Config)
	driver := state.Get("driver").(parallelscommon.Driver)
	ui := state.Get("ui").(packer.Ui)
	name := config.VMName

	command := []string{
		"create", name,
		"--distribution", config.GuestOSType,
		"--dst", config.OutputDir,
		"--no-hdd",
	}

	ui.Say("Creating virtual machine...")
	if err := driver.Prlctl(command...); err != nil {
		err := fmt.Errorf("Error creating VM: %s", err)
		state.Put("error", err)
		ui.Error(err.Error())
		return multistep.ActionHalt
	}

	ui.Say("Applying default settings...")
	if err := driver.SetDefaultConfiguration(name); err != nil {
		err := fmt.Errorf("Error VM configuration: %s", err)
		state.Put("error", err)
		ui.Error(err.Error())
		return multistep.ActionHalt
	}

	// Set the VM name property on the first command
	if s.vmName == "" {
		s.vmName = name
	}

	// Set the final name in the state bag so others can use it
	state.Put("vmName", s.vmName)
	return multistep.ActionContinue
}

func (s *stepCreateVM) Cleanup(state multistep.StateBag) {
	if s.vmName == "" {
		return
	}

	driver := state.Get("driver").(parallelscommon.Driver)
	ui := state.Get("ui").(packer.Ui)

	ui.Say("Unregistering virtual machine...")
	if err := driver.Prlctl("unregister", s.vmName); err != nil {
		ui.Error(fmt.Sprintf("Error unregistering virtual machine: %s", err))
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 Dorival Pedroso and Raul Durand. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package rnd

import "github.com/cpmech/gosl/chk"

// DistType indicates the distribution to which a random variable appears to belong to
type DistType int

const (
	D_Normal    DistType = iota + 1 // normal
	D_Lognormal                     // lognormal
	D_Gumbel                        // Type I Extreme Value
	D_Frechet                       // Type II Extreme Value
)

// VarData implements data defining one random variable
type VarData struct {

	// input
	D DistType // type of distribution
	M float64  // mean
	S float64  // standard deviation

	// input: Frechet
	L float64 // location
	C float64 // scale
	A float64 // shape

	// derived
	distr Distribution // pointer to distribution
}

// Transform transform x into standard normal space
func (o *VarData) Transform(x float64) (y float64, invalid bool) {
	if o.D == D_Normal {
		y = (x - o.M) / o.S
		return
	}
	F := o.distr.Cdf(x)
	if F == 0 || F == 1 { // y = Φ⁻¹(F) → -∞ or +∞
		invalid = true
		return
	}
	y = StdInvPhi(F)
	return
}

// Variables implements a set of random variables
type Variables []*VarData

// Init initialises distributions in Variables
func (o *Variables) Init() (err error) {
	for _, d := range *o {
		d.distr, err = GetDistrib(d.D)
		if err != nil {
			chk.Err("cannot find distribution:\n%v", err)
			return
		}
		err = d.distr.Init(d)
		if err != nil {
			chk.Err("cannot initialise variables:\n%v", err)
			return
		}
	}
	return
}

// Transform transforms all variables
func (o Variables) Transform(x []float64) (y []float64, invalid bool) {
	y = make([]float64, len(x))
	for i, d := range o {
		y[i], invalid = d.Transform(x[i])
		if invalid {
			return
		}
	}
	return
}
<commit_msg>optional data added to Random Variables; e.g. for optimisation<commit_after>// Copyright 2015 Dorival Pedroso and Raul Durand. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package rnd

import "github.com/cpmech/gosl/chk"

// DistType indicates the distribution to which a random variable appears to belong to
type DistType int

const (
	D_Normal    DistType = iota + 1 // normal
	D_Lognormal                     // lognormal
	D_Gumbel                        // Type I Extreme Value
	D_Frechet                       // Type II Extreme Value
)

// VarData implements data defining one random variable
type VarData struct {

	// input
	D DistType // type of distribution
	M float64  // mean
	S float64  // standard deviation

	// input: Frechet
	L float64 // location
	C float64 // scale
	A float64 // shape

	// optional
	Name string  // name of this random variable; e.g. 'λ', 'κ', 'load', etc.
	Min  float64 // min value
	Max  float64 // max value

	// derived
	distr Distribution // pointer to distribution
}

// Transform transform x into standard normal space
func (o *VarData) Transform(x float64) (y float64, invalid bool) {
	if o.D == D_Normal {
		y = (x - o.M) / o.S
		return
	}
	F := o.distr.Cdf(x)
	if F == 0 || F == 1 { // y = Φ⁻¹(F) → -∞ or +∞
		invalid = true
		return
	}
	y = StdInvPhi(F)
	return
}

// Variables implements a set of random variables
type Variables []*VarData

// Init initialises distributions in Variables
func (o *Variables) Init() (err error) {
	for _, d := range *o {
		d.distr, err = GetDistrib(d.D)
		if err != nil {
			chk.Err("cannot find distribution:\n%v", err)
			return
		}
		err = d.distr.Init(d)
		if err != nil {
			chk.Err("cannot initialise variables:\n%v", err)
			return
		}
	}
	return
}

// Transform transforms all variables
func (o Variables) Transform(x []float64) (y []float64, invalid bool) {
	y = make([]float64, len(x))
	for i, d := range o {
		y[i], invalid = d.Transform(x[i])
		if invalid {
			return
		}
	}
	return
}

// GetDistribution returns distribution ID from name
func GetDistribution(name string) DistType {
	switch name {
	case "normal":
		return D_Normal
	case "lognormal":
		return D_Lognormal
	case "gumbel":
		return D_Gumbel
	case "frechet":
		return D_Frechet
	default:
		chk.Panic("cannot get distribution named %q", name)
	}
	return D_Normal
}
<|endoftext|>


<commit_before>package router

import (
	"github.com/gorilla/mux"

	"net/http"
)

var Router *mux.Router

func init()  {
	Router = mux.NewRouter()

	cssHandler := http.FileServer(http.Dir("./css/"))
	jsHandler := http.FileServer(http.Dir("./js/"))
	imgHandler := http.FileServer(http.Dir("./img/"))
	http.Handle("/css/", http.StripPrefix("/css/", cssHandler))
	http.Handle("/js/", http.StripPrefix("/js/", jsHandler))
	http.Handle("/img/", http.StripPrefix("/img/", imgHandler))

	// Routes,
	Router.HandleFunc("/", HomeHandler).Name("home")
	Router.HandleFunc("/page/{page:[0-9]+}", HomeHandler).Name("home_page")
	Router.HandleFunc("/search", SearchHandler).Name("search")
	Router.HandleFunc("/search/{page}", SearchHandler).Name("search_page")
	Router.HandleFunc("/api/{page}", ApiHandler).Methods("GET")
	Router.HandleFunc("/api/view/{id}", ApiViewHandler).Methods("GET")
	Router.HandleFunc("/faq", FaqHandler).Name("faq")
	Router.HandleFunc("/feed.xml", RssHandler).Name("feed")
	Router.HandleFunc("/view/{id}", ViewHandler).Name("view_torrent")
}<commit_msg>Correct feed url<commit_after>package router

import (
	"github.com/gorilla/mux"

	"net/http"
)

var Router *mux.Router

func init() {
	Router = mux.NewRouter()

	cssHandler := http.FileServer(http.Dir("./css/"))
	jsHandler := http.FileServer(http.Dir("./js/"))
	imgHandler := http.FileServer(http.Dir("./img/"))
	http.Handle("/css/", http.StripPrefix("/css/", cssHandler))
	http.Handle("/js/", http.StripPrefix("/js/", jsHandler))
	http.Handle("/img/", http.StripPrefix("/img/", imgHandler))

	// Routes,
	Router.HandleFunc("/", HomeHandler).Name("home")
	Router.HandleFunc("/page/{page:[0-9]+}", HomeHandler).Name("home_page")
	Router.HandleFunc("/search", SearchHandler).Name("search")
	Router.HandleFunc("/search/{page}", SearchHandler).Name("search_page")
	Router.HandleFunc("/api/{page}", ApiHandler).Methods("GET")
	Router.HandleFunc("/api/view/{id}", ApiViewHandler).Methods("GET")
	Router.HandleFunc("/faq", FaqHandler).Name("faq")
	Router.HandleFunc("/feed", RssHandler).Name("feed")
	Router.HandleFunc("/view/{id}", ViewHandler).Name("view_torrent")
}
<|endoftext|>


<commit_before>/*
Copyright 2017 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 impliem.
See the License for the specific language governing permissions and
limitations under the License.
*/

package main

import (
	"bytes"
	"flag"
	"fmt"
	"os"
	"reflect"
	"testing"
	"time"

	"k8s.io/ip-masq-agent/cmd/ip-masq-agent/testing/fakefs"
	utiliptables "k8s.io/kubernetes/pkg/util/iptables"
	iptest "k8s.io/kubernetes/pkg/util/iptables/testing"
)

// turn off glog logging during tests to avoid clutter in output
func TestMain(m *testing.M) {
	flag.Set("logtostderr", "false")
	ec := m.Run()
	os.Exit(ec)
}

// returns a MasqDaemon with empty config values and a fake iptables interface
func NewFakeMasqDaemon() *MasqDaemon {
	return &MasqDaemon{
		config:   &MasqConfig{},
		iptables: iptest.NewFake(),
	}
}

// specs for testing config validation
var validateConfigTests = []struct {
	cfg *MasqConfig
	err error
}{
	// Empty CIDR List
	{&MasqConfig{}, nil},
	// Default Config
	{NewMasqConfig(), nil},
	// CIDR that doesn't match regex
	{&MasqConfig{NonMasqueradeCIDRs: []string{"abcdefg"}}, fmt.Errorf(cidrMatchErrFmt, "abcdefg", cidrRE)},
	// Multiple CIDRs, one doesn't match regex
	{&MasqConfig{NonMasqueradeCIDRs: []string{"10.0.0.0/8", "abcdefg"}}, fmt.Errorf(cidrMatchErrFmt, "abcdefg", cidrRE)},
	// CIDR that matches regex but can't be parsed
	{&MasqConfig{NonMasqueradeCIDRs: []string{"10.256.0.0/16"}}, fmt.Errorf(cidrParseErrFmt, "10.256.0.0/16", fmt.Errorf("invalid CIDR address: 10.256.0.0/16"))},
	// Misaligned CIDR
	{&MasqConfig{NonMasqueradeCIDRs: []string{"10.0.0.1/8"}}, fmt.Errorf(cidrAlignErrFmt, "10.0.0.1/8", "10.0.0.1", "10.0.0.0/8")},
}

// tests the MasqConfig.validate method
func TestConfigValidate(t *testing.T) {
	for _, tt := range validateConfigTests {
		err := tt.cfg.validate()
		if errorToString(err) != errorToString(tt.err) {
			t.Errorf("%+v.validate() => %s, want %s", tt.cfg, errorToString(err), errorToString(tt.err))
		}
	}
}

// specs for testing loading config from fs
var syncConfigTests = []struct {
	desc string            // human readable description of the fs used for the test e.g. "no config file"
	fs   fakefs.FileSystem // filesystem interface
	err  error             // expected error from MasqDaemon.syncConfig(fs)
	cfg  *MasqConfig       // expected values of the configuration after loading from fs
}{
	// valid yaml
	{"valid yaml file, all keys", fakefs.StringFS{`
nonMasqueradeCIDRs:
  - 172.16.0.0/12
  - 10.0.0.0/8
masqLinkLocal: true
resyncInterval: 5s
`}, nil, &MasqConfig{
		NonMasqueradeCIDRs: []string{"172.16.0.0/12", "10.0.0.0/8"},
		MasqLinkLocal:      true,
		ResyncInterval:     Duration(5 * time.Second)}},

	{"valid yaml file, just nonMasqueradeCIDRs", fakefs.StringFS{`
nonMasqueradeCIDRs:
  - 192.168.0.0/16
`}, nil, &MasqConfig{
		NonMasqueradeCIDRs: []string{"192.168.0.0/16"},
		MasqLinkLocal:      NewMasqConfig().MasqLinkLocal,
		ResyncInterval:     NewMasqConfig().ResyncInterval}},

	{"valid yaml file, just masqLinkLocal", fakefs.StringFS{`
masqLinkLocal: true
`}, nil, &MasqConfig{
		NonMasqueradeCIDRs: NewMasqConfig().NonMasqueradeCIDRs,
		MasqLinkLocal:      true,
		ResyncInterval:     NewMasqConfig().ResyncInterval}},

	{"valid yaml file, just resyncInterval", fakefs.StringFS{`
resyncInterval: 5m
`}, nil, &MasqConfig{
		NonMasqueradeCIDRs: NewMasqConfig().NonMasqueradeCIDRs,
		MasqLinkLocal:      NewMasqConfig().MasqLinkLocal,
		ResyncInterval:     Duration(5 * time.Minute)}},

	// invalid yaml
	{"invalid yaml file", fakefs.StringFS{`*`}, fmt.Errorf("yaml: did not find expected alphabetic or numeric character"), NewMasqConfig()},

	// valid json
	{"valid json file, all keys", fakefs.StringFS{`
{
  "nonMasqueradeCIDRs": ["172.16.0.0/12", "10.0.0.0/8"],
  "masqLinkLocal": true,
  "resyncInterval": "5s"
}
`},
		nil, &MasqConfig{
			NonMasqueradeCIDRs: []string{"172.16.0.0/12", "10.0.0.0/8"},
			MasqLinkLocal:      true,
			ResyncInterval:     Duration(5 * time.Second)}},

	{"valid json file, just nonMasqueradeCIDRs", fakefs.StringFS{`
{
	"nonMasqueradeCIDRs": ["192.168.0.0/16"]
}
`},
		nil, &MasqConfig{
			NonMasqueradeCIDRs: []string{"192.168.0.0/16"},
			MasqLinkLocal:      NewMasqConfig().MasqLinkLocal,
			ResyncInterval:     NewMasqConfig().ResyncInterval}},

	{"valid json file, just masqLinkLocal", fakefs.StringFS{`
{
	"masqLinkLocal": true
}
`},
		nil, &MasqConfig{
			NonMasqueradeCIDRs: NewMasqConfig().NonMasqueradeCIDRs,
			MasqLinkLocal:      true,
			ResyncInterval:     NewMasqConfig().ResyncInterval}},

	{"valid json file, just resyncInterval", fakefs.StringFS{`
{
	"resyncInterval": "5m"
}
`},
		nil, &MasqConfig{
			NonMasqueradeCIDRs: NewMasqConfig().NonMasqueradeCIDRs,
			MasqLinkLocal:      NewMasqConfig().MasqLinkLocal,
			ResyncInterval:     Duration(5 * time.Minute)}},

	// invalid json
	{"invalid json file", fakefs.StringFS{`{*`}, fmt.Errorf("invalid character '*' looking for beginning of object key string"), NewMasqConfig()},

	// file does not exist
	{"no config file", fakefs.NotExistFS{}, nil, NewMasqConfig()}, // If the file does not exist, defaults should be used
}

// tests MasqDaemon.syncConfig
func TestSyncConfig(t *testing.T) {
	for _, tt := range syncConfigTests {
		m := NewFakeMasqDaemon()
		m.config = NewMasqConfig()
		err := m.syncConfig(tt.fs)
		if errorToString(err) != errorToString(tt.err) {
			t.Errorf("MasqDaemon.syncConfig(fs: %s) => %s, want %s", tt.desc, errorToString(err), errorToString(tt.err))
		} else if !reflect.DeepEqual(m.config, tt.cfg) {
			t.Errorf("MasqDaemon.syncConfig(fs: %s) loaded as %+v, want %+v", tt.desc, m.config, tt.cfg)
		}
	}
}

// tests MasqDaemon.syncMasqRules
func TestSyncMasqRules(t *testing.T) {
	// empty config
	m := NewFakeMasqDaemon()
	want := `*nat
:` + string(masqChain) + ` - [0:0]
-A ` + string(masqChain) + ` ` + nonMasqRuleComment + ` -m addrtype ! --dst-type LOCAL -d 169.254.0.0/16 -j RETURN
-A ` + string(masqChain) + ` ` + masqRuleComment + ` -m addrtype ! --dst-type LOCAL -j MASQUERADE
COMMIT
`
	m.syncMasqRules()
	fipt, ok := m.iptables.(*iptest.FakeIPTables)
	if !ok {
		t.Errorf("MasqDaemon wasn't using the expected iptables mock")
	}
	if string(fipt.Lines) != want {
		t.Errorf("syncMasqRules wrote %q, want %q", string(fipt.Lines), want)
	}

	// default config
	m = NewFakeMasqDaemon()
	m.config = NewMasqConfig()
	want = `*nat
:` + string(masqChain) + ` - [0:0]
-A ` + string(masqChain) + ` ` + nonMasqRuleComment + ` -m addrtype ! --dst-type LOCAL -d 169.254.0.0/16 -j RETURN
-A ` + string(masqChain) + ` ` + nonMasqRuleComment + ` -m addrtype ! --dst-type LOCAL -d 10.0.0.0/8 -j RETURN
-A ` + string(masqChain) + ` ` + nonMasqRuleComment + ` -m addrtype ! --dst-type LOCAL -d 172.16.0.0/12 -j RETURN
-A ` + string(masqChain) + ` ` + nonMasqRuleComment + ` -m addrtype ! --dst-type LOCAL -d 192.168.0.0/16 -j RETURN
-A ` + string(masqChain) + ` ` + masqRuleComment + ` -m addrtype ! --dst-type LOCAL -j MASQUERADE
COMMIT
`
	m.syncMasqRules()
	fipt, ok = m.iptables.(*iptest.FakeIPTables)
	if !ok {
		t.Errorf("MasqDaemon wasn't using the expected iptables mock")
	}
	if string(fipt.Lines) != want {
		t.Errorf("syncMasqRules wrote %q, want %q", string(fipt.Lines), want)
	}
}

// TODO(mtaufen): switch to an iptables mock that allows us to check the results of EnsureRule
// tests m.ensurePostroutingJump
func TestEnsurePostroutingJump(t *testing.T) {
	m := NewFakeMasqDaemon()
	if err := m.ensurePostroutingJump(); err != nil {
		t.Errorf("error: %v", err)
	}
}

// tests writeNonMasqRule
func TestWriteNonMasqRule(t *testing.T) {
	lines := bytes.NewBuffer(nil)
	cidr := "10.0.0.0/8"
	want := string(utiliptables.Append) + " " + string(masqChain) +
		` -m comment --comment "ip-masq-agent: cluster-local traffic should not be subject to MASQUERADE"` +
		" -m addrtype ! --dst-type LOCAL -d " + cidr + " -j RETURN\n"
	writeNonMasqRule(lines, cidr)

	s, err := lines.ReadString('\n')
	if err != nil {
		t.Error("writeRule did not append a newline")
	}
	if s != want {
		t.Errorf("writeNonMasqRule(lines, "+cidr+") wrote %q, want %q", s, want)
	}
}

// tests writeRule
func TestWriteRule(t *testing.T) {
	lines := bytes.NewBuffer(nil)
	want := string(utiliptables.Append) + " " + string(masqChain) +
		" -m comment --comment \"test writing a rule\"\n"
	writeRule(lines, utiliptables.Append, masqChain, "-m", "comment", "--comment", `"test writing a rule"`)

	s, err := lines.ReadString('\n')
	if err != nil {
		t.Error("writeRule did not append a newline")
	}
	if s != want {
		t.Errorf("writeRule(lines, pos, chain, \"-m\", \"comment\", \"--comment\", `\"test writing a rule\"`) wrote %q, want %q", s, want)
	}
}

// tests writeLine
func TestWriteLine(t *testing.T) {
	lines := bytes.NewBuffer(nil)
	want := "a b c\n"

	writeLine(lines, "a", "b", "c")

	s, err := lines.ReadString('\n')
	if err != nil {
		t.Error("writeLine did not append a newline")
	}
	if s != want {
		t.Errorf("writeLine(lines, \"a\", \"b\", \"c\") wrote %q, want %q", s, want)
	}
}

// convert error to string, while also handling nil errors
func errorToString(err error) string {
	if err == nil {
		return "nil error"
	}
	return fmt.Sprintf("error %q", err.Error())
}
<commit_msg>fix tests<commit_after>/*
Copyright 2017 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 impliem.
See the License for the specific language governing permissions and
limitations under the License.
*/

package main

import (
	"bytes"
	"flag"
	"fmt"
	"os"
	"reflect"
	"testing"
	"time"

	"k8s.io/ip-masq-agent/cmd/ip-masq-agent/testing/fakefs"
	utiliptables "k8s.io/kubernetes/pkg/util/iptables"
	iptest "k8s.io/kubernetes/pkg/util/iptables/testing"
)

// turn off glog logging during tests to avoid clutter in output
func TestMain(m *testing.M) {
	flag.Set("logtostderr", "false")
	ec := m.Run()
	os.Exit(ec)
}

// returns a MasqDaemon with empty config values and a fake iptables interface
func NewFakeMasqDaemon() *MasqDaemon {
	return &MasqDaemon{
		config:   &MasqConfig{},
		iptables: iptest.NewFake(),
	}
}

// specs for testing config validation
var validateConfigTests = []struct {
	cfg *MasqConfig
	err error
}{
	// Empty CIDR List
	{&MasqConfig{}, nil},
	// Default Config
	{NewMasqConfig(), nil},
	// CIDR that doesn't match regex
	{&MasqConfig{NonMasqueradeCIDRs: []string{"abcdefg"}}, fmt.Errorf(cidrMatchErrFmt, "abcdefg", cidrRE)},
	// Multiple CIDRs, one doesn't match regex
	{&MasqConfig{NonMasqueradeCIDRs: []string{"10.0.0.0/8", "abcdefg"}}, fmt.Errorf(cidrMatchErrFmt, "abcdefg", cidrRE)},
	// CIDR that matches regex but can't be parsed
	{&MasqConfig{NonMasqueradeCIDRs: []string{"10.256.0.0/16"}}, fmt.Errorf(cidrParseErrFmt, "10.256.0.0/16", fmt.Errorf("invalid CIDR address: 10.256.0.0/16"))},
	// Misaligned CIDR
	{&MasqConfig{NonMasqueradeCIDRs: []string{"10.0.0.1/8"}}, fmt.Errorf(cidrAlignErrFmt, "10.0.0.1/8", "10.0.0.1", "10.0.0.0/8")},
}

// tests the MasqConfig.validate method
func TestConfigValidate(t *testing.T) {
	for _, tt := range validateConfigTests {
		err := tt.cfg.validate()
		if errorToString(err) != errorToString(tt.err) {
			t.Errorf("%+v.validate() => %s, want %s", tt.cfg, errorToString(err), errorToString(tt.err))
		}
	}
}

// specs for testing loading config from fs
var syncConfigTests = []struct {
	desc string            // human readable description of the fs used for the test e.g. "no config file"
	fs   fakefs.FileSystem // filesystem interface
	err  error             // expected error from MasqDaemon.syncConfig(fs)
	cfg  *MasqConfig       // expected values of the configuration after loading from fs
}{
	// valid yaml
	{"valid yaml file, all keys", fakefs.StringFS{`
nonMasqueradeCIDRs:
  - 172.16.0.0/12
  - 10.0.0.0/8
masqLinkLocal: true
resyncInterval: 5s
`}, nil, &MasqConfig{
		NonMasqueradeCIDRs: []string{"172.16.0.0/12", "10.0.0.0/8"},
		MasqLinkLocal:      true,
		ResyncInterval:     Duration(5 * time.Second)}},

	{"valid yaml file, just nonMasqueradeCIDRs", fakefs.StringFS{`
nonMasqueradeCIDRs:
  - 192.168.0.0/16
`}, nil, &MasqConfig{
		NonMasqueradeCIDRs: []string{"192.168.0.0/16"},
		MasqLinkLocal:      NewMasqConfig().MasqLinkLocal,
		ResyncInterval:     NewMasqConfig().ResyncInterval}},

	{"valid yaml file, just masqLinkLocal", fakefs.StringFS{`
masqLinkLocal: true
`}, nil, &MasqConfig{
		NonMasqueradeCIDRs: NewMasqConfig().NonMasqueradeCIDRs,
		MasqLinkLocal:      true,
		ResyncInterval:     NewMasqConfig().ResyncInterval}},

	{"valid yaml file, just resyncInterval", fakefs.StringFS{`
resyncInterval: 5m
`}, nil, &MasqConfig{
		NonMasqueradeCIDRs: NewMasqConfig().NonMasqueradeCIDRs,
		MasqLinkLocal:      NewMasqConfig().MasqLinkLocal,
		ResyncInterval:     Duration(5 * time.Minute)}},

	// invalid yaml
	{"invalid yaml file", fakefs.StringFS{`*`}, fmt.Errorf("yaml: did not find expected alphabetic or numeric character"), NewMasqConfig()},

	// valid json
	{"valid json file, all keys", fakefs.StringFS{`
{
  "nonMasqueradeCIDRs": ["172.16.0.0/12", "10.0.0.0/8"],
  "masqLinkLocal": true,
  "resyncInterval": "5s"
}
`},
		nil, &MasqConfig{
			NonMasqueradeCIDRs: []string{"172.16.0.0/12", "10.0.0.0/8"},
			MasqLinkLocal:      true,
			ResyncInterval:     Duration(5 * time.Second)}},

	{"valid json file, just nonMasqueradeCIDRs", fakefs.StringFS{`
{
	"nonMasqueradeCIDRs": ["192.168.0.0/16"]
}
`},
		nil, &MasqConfig{
			NonMasqueradeCIDRs: []string{"192.168.0.0/16"},
			MasqLinkLocal:      NewMasqConfig().MasqLinkLocal,
			ResyncInterval:     NewMasqConfig().ResyncInterval}},

	{"valid json file, just masqLinkLocal", fakefs.StringFS{`
{
	"masqLinkLocal": true
}
`},
		nil, &MasqConfig{
			NonMasqueradeCIDRs: NewMasqConfig().NonMasqueradeCIDRs,
			MasqLinkLocal:      true,
			ResyncInterval:     NewMasqConfig().ResyncInterval}},

	{"valid json file, just resyncInterval", fakefs.StringFS{`
{
	"resyncInterval": "5m"
}
`},
		nil, &MasqConfig{
			NonMasqueradeCIDRs: NewMasqConfig().NonMasqueradeCIDRs,
			MasqLinkLocal:      NewMasqConfig().MasqLinkLocal,
			ResyncInterval:     Duration(5 * time.Minute)}},

	// invalid json
	{"invalid json file", fakefs.StringFS{`{*`}, fmt.Errorf("invalid character '*' looking for beginning of object key string"), NewMasqConfig()},

	// file does not exist
	{"no config file", fakefs.NotExistFS{}, nil, NewMasqConfig()}, // If the file does not exist, defaults should be used
}

// tests MasqDaemon.syncConfig
func TestSyncConfig(t *testing.T) {
	for _, tt := range syncConfigTests {
		m := NewFakeMasqDaemon()
		m.config = NewMasqConfig()
		err := m.syncConfig(tt.fs)
		if errorToString(err) != errorToString(tt.err) {
			t.Errorf("MasqDaemon.syncConfig(fs: %s) => %s, want %s", tt.desc, errorToString(err), errorToString(tt.err))
		} else if !reflect.DeepEqual(m.config, tt.cfg) {
			t.Errorf("MasqDaemon.syncConfig(fs: %s) loaded as %+v, want %+v", tt.desc, m.config, tt.cfg)
		}
	}
}

// tests MasqDaemon.syncMasqRules
func TestSyncMasqRules(t *testing.T) {
	// empty config
	m := NewFakeMasqDaemon()
	want := `*nat
:` + string(masqChain) + ` - [0:0]
-A ` + string(masqChain) + ` ` + nonMasqRuleComment + ` -d 169.254.0.0/16 -j RETURN
-A ` + string(masqChain) + ` ` + masqRuleComment + ` -j MASQUERADE
COMMIT
`
	m.syncMasqRules()
	fipt, ok := m.iptables.(*iptest.FakeIPTables)
	if !ok {
		t.Errorf("MasqDaemon wasn't using the expected iptables mock")
	}
	if string(fipt.Lines) != want {
		t.Errorf("syncMasqRules wrote %q, want %q", string(fipt.Lines), want)
	}

	// default config
	m = NewFakeMasqDaemon()
	m.config = NewMasqConfig()
	want = `*nat
:` + string(masqChain) + ` - [0:0]
-A ` + string(masqChain) + ` ` + nonMasqRuleComment + ` -d 169.254.0.0/16 -j RETURN
-A ` + string(masqChain) + ` ` + nonMasqRuleComment + ` -d 10.0.0.0/8 -j RETURN
-A ` + string(masqChain) + ` ` + nonMasqRuleComment + ` -d 172.16.0.0/12 -j RETURN
-A ` + string(masqChain) + ` ` + nonMasqRuleComment + ` -d 192.168.0.0/16 -j RETURN
-A ` + string(masqChain) + ` ` + masqRuleComment + ` -j MASQUERADE
COMMIT
`
	m.syncMasqRules()
	fipt, ok = m.iptables.(*iptest.FakeIPTables)
	if !ok {
		t.Errorf("MasqDaemon wasn't using the expected iptables mock")
	}
	if string(fipt.Lines) != want {
		t.Errorf("syncMasqRules wrote %q, want %q", string(fipt.Lines), want)
	}
}

// TODO(mtaufen): switch to an iptables mock that allows us to check the results of EnsureRule
// tests m.ensurePostroutingJump
func TestEnsurePostroutingJump(t *testing.T) {
	m := NewFakeMasqDaemon()
	if err := m.ensurePostroutingJump(); err != nil {
		t.Errorf("error: %v", err)
	}
}

// tests writeNonMasqRule
func TestWriteNonMasqRule(t *testing.T) {
	lines := bytes.NewBuffer(nil)
	cidr := "10.0.0.0/8"
	want := string(utiliptables.Append) + " " + string(masqChain) +
		` -m comment --comment "ip-masq-agent: local traffic is not subject to MASQUERADE"` +
		" -d " + cidr + " -j RETURN\n"
	writeNonMasqRule(lines, cidr)

	s, err := lines.ReadString('\n')
	if err != nil {
		t.Error("writeRule did not append a newline")
	}
	if s != want {
		t.Errorf("writeNonMasqRule(lines, "+cidr+"):\n   got: %q\n  want: %q", s, want)
	}
}

// tests writeRule
func TestWriteRule(t *testing.T) {
	lines := bytes.NewBuffer(nil)
	want := string(utiliptables.Append) + " " + string(masqChain) +
		" -m comment --comment \"test writing a rule\"\n"
	writeRule(lines, utiliptables.Append, masqChain, "-m", "comment", "--comment", `"test writing a rule"`)

	s, err := lines.ReadString('\n')
	if err != nil {
		t.Error("writeRule did not append a newline")
	}
	if s != want {
		t.Errorf("writeRule(lines, pos, chain, \"-m\", \"comment\", \"--comment\", `\"test writing a rule\"`) wrote %q, want %q", s, want)
	}
}

// tests writeLine
func TestWriteLine(t *testing.T) {
	lines := bytes.NewBuffer(nil)
	want := "a b c\n"

	writeLine(lines, "a", "b", "c")

	s, err := lines.ReadString('\n')
	if err != nil {
		t.Error("writeLine did not append a newline")
	}
	if s != want {
		t.Errorf("writeLine(lines, \"a\", \"b\", \"c\") wrote %q, want %q", s, want)
	}
}

// convert error to string, while also handling nil errors
func errorToString(err error) string {
	if err == nil {
		return "nil error"
	}
	return fmt.Sprintf("error %q", err.Error())
}
<|endoftext|>