<commit_before>// Copyright 2011 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// 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 ogletest

import (
	"bytes"
	"flag"
	"fmt"
	"path"
	"regexp"
	"runtime"
	"sync"
	"testing"
	"time"
)

var testFilter = flag.String("ogletest.run", "", "Regexp for matching tests to run.")

// runTestsOnce protects RunTests from executing multiple times.
var runTestsOnce sync.Once

func isAbortError(x interface{}) bool {
	_, ok := x.(abortError)
	return ok
}

// Run a single test function, returning a slice of failure records.
func runTestFunction(tf TestFunction) (failures []FailureRecord) {
	// Set up a clean slate for this test. Make sure to reset it after everything
	// below is finished, so we don't accidentally use it elsewhere.
	currentlyRunningTest = newTestInfo()
	defer func() {
		currentlyRunningTest = nil
	}()

	// Run the SetUp function, if any, paying attention to whether it panics.
	setUpPanicked := false
	if tf.SetUp != nil {
		setUpPanicked = runWithProtection(func() { tf.SetUp(currentlyRunningTest) })
	}

	// Run the test function itself, but only if the SetUp function didn't panic.
	// (This includes AssertThat errors.)
	if !setUpPanicked {
		runWithProtection(tf.Run)
	}

	// Run the TearDown function, if any.
	if tf.TearDown != nil {
		runWithProtection(tf.TearDown)
	}

	// Tell the mock controller for the tests to report any errors it's sitting
	// on.
	currentlyRunningTest.MockController.Finish()

	return currentlyRunningTest.failureRecords
}

// Run everything registered with Register (including via the wrapper
// RegisterTestSuite).
//
// Failures are communicated to the supplied testing.T object. This is the
// bridge between ogletest and the testing package (and `go test`); you should
// ensure that it's called at least once by creating a test function compatible
// with `go test` and calling it there.
//
// For example:
//
//     import (
//       "github.com/jacobsa/ogletest"
//       "testing"
//     )
//
//     func TestOgletest(t *testing.T) {
//       ogletest.RunTests(t)
//     }
//
func RunTests(t *testing.T) {
	runTestsOnce.Do(func() { runTestsInternal(t) })
}

// runTestsInternal does the real work of RunTests, which simply wraps it in a
// sync.Once.
func runTestsInternal(t *testing.T) {
	// Process each registered suite.
	for _, suite := range registeredSuites {
		fmt.Printf("[----------] Running tests from %s\n", suite.Name)

		// Run the SetUp function, if any.
		if suite.SetUp != nil {
			suite.SetUp()
		}

		// Run each test function that the user has not told us to skip.
		for _, tf := range filterTestFunctions(suite) {
			// Print a banner for the start of this test function.
			fmt.Printf("[ RUN      ] %s.%s\n", suite.Name, tf.Name)

			// Run the test function.
			startTime := time.Now()
			failures := runTestFunction(tf)
			runDuration := time.Since(startTime)

			// Print any failures, and mark the test as having failed if there are any.
			for _, record := range failures {
				t.Fail()
				fmt.Printf(
					"%s:%d:\n%s\n\n",
					record.FileName,
					record.LineNumber,
					record.Error)
			}

			// Print a banner for the end of the test.
			bannerMessage := "[       OK ]"
			if len(failures) != 0 {
				bannerMessage = "[  FAILED  ]"
			}

			// Print a summary of the time taken, if long enough.
			var timeMessage string
			if runDuration >= 25*time.Millisecond {
				timeMessage = fmt.Sprintf(" (%s)", runDuration.String())
			}

			fmt.Printf(
				"%s %s.%s%s\n",
				bannerMessage,
				suite.Name,
				tf.Name,
				timeMessage)
		}

		// Run the suite's TearDown function, if any.
		if suite.TearDown != nil {
			suite.TearDown()
		}

		fmt.Printf("[----------] Finished with tests from %s\n", suite.Name)
	}
}

// Return true iff the supplied program counter appears to lie within panic().
func isPanic(pc uintptr) bool {
	f := runtime.FuncForPC(pc)
	if f == nil {
		return false
	}

	return f.Name() == "runtime.gopanic" || f.Name() == "runtime.sigpanic"
}

// Find the deepest stack frame containing something that appears to be a
// panic. Return the 'skip' value that a caller to this function would need
// to supply to runtime.Caller for that frame, or a negative number if not found.
func findPanic() int {
	localSkip := -1
	for i := 0; ; i++ {
		// Stop if we've passed the base of the stack.
		pc, _, _, ok := runtime.Caller(i)
		if !ok {
			break
		}

		// Is this a panic?
		if isPanic(pc) {
			localSkip = i
		}
	}

	return localSkip - 1
}

// Attempt to find the file base name and line number for the ultimate source
// of a panic, on the panicking stack. Return a human-readable sentinel if
// unsuccessful.
func findPanicFileLine() (string, int) {
	panicSkip := findPanic()
	if panicSkip < 0 {
		return "(unknown)", 0
	}

	// Find the trigger of the panic.
	_, file, line, ok := runtime.Caller(panicSkip + 1)
	if !ok {
		return "(unknown)", 0
	}

	return path.Base(file), line
}

// Run the supplied function, catching panics (including AssertThat errors) and
// reporting them to the currently-running test as appropriate. Return true iff
// the function panicked.
func runWithProtection(f func()) (panicked bool) {
	defer func() {
		// If the test didn't panic, we're done.
		r := recover()
		if r == nil {
			return
		}

		panicked = true

		// We modify the currently running test below.
		currentlyRunningTest.mu.Lock()
		defer currentlyRunningTest.mu.Unlock()

		// If the function panicked (and the panic was not due to an AssertThat
		// failure), add a failure for the panic.
		if !isAbortError(r) {
			var panicRecord FailureRecord
			panicRecord.FileName, panicRecord.LineNumber = findPanicFileLine()
			panicRecord.Error = fmt.Sprintf(
				"panic: %v\n\n%s", r, formatPanicStack())

			currentlyRunningTest.failureRecords = append(
				currentlyRunningTest.failureRecords,
				panicRecord)
		}
	}()

	f()
	return
}

func formatPanicStack() string {
	buf := new(bytes.Buffer)

	// Find the panic. If successful, we'll skip to below it. Otherwise, we'll
	// format everything.
	var initialSkip int
	if panicSkip := findPanic(); panicSkip >= 0 {
		initialSkip = panicSkip + 1
	}

	for i := initialSkip; ; i++ {
		pc, file, line, ok := runtime.Caller(i)
		if !ok {
			break
		}

		// Choose a function name to display.
		funcName := "(unknown)"
		if f := runtime.FuncForPC(pc); f != nil {
			funcName = f.Name()
		}

		// Stop if we've gotten as far as the test runner code.
		if funcName == "github.com/jacobsa/ogletest.runTestMethod" ||
			funcName == "github.com/jacobsa/ogletest.runWithProtection" {
			break
		}

		// Add an entry for this frame.
		fmt.Fprintf(buf, "%s\n\t%s:%d\n", funcName, file, line)
	}

	return buf.String()
}

// Filter test functions according to the user-supplied filter flag.
func filterTestFunctions(suite TestSuite) (out []TestFunction) {
	re, err := regexp.Compile(*testFilter)
	if err != nil {
		panic("Invalid value for --ogletest.run: " + err.Error())
	}

	for _, tf := range suite.TestFunctions {
		fullName := fmt.Sprintf("%s.%s", suite.Name, tf.Name)
		if !re.MatchString(fullName) {
			continue
		}

		out = append(out, tf)
	}

	return
}
<commit_msg>Added a flag that allows for stopping early on failure.<commit_after>// Copyright 2011 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// 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 ogletest

import (
	"bytes"
	"flag"
	"fmt"
	"path"
	"regexp"
	"runtime"
	"sync"
	"testing"
	"time"
)

var fTestFilter = flag.String(
	"ogletest.run",
	"",
	"Regexp for matching tests to run.")

var fStopEarly = flag.Bool(
	"ogletest.stop_early",
	false,
	"If true, stop after the first failure.")

// runTestsOnce protects RunTests from executing multiple times.
var runTestsOnce sync.Once

func isAbortError(x interface{}) bool {
	_, ok := x.(abortError)
	return ok
}

// Run a single test function, returning a slice of failure records.
func runTestFunction(tf TestFunction) (failures []FailureRecord) {
	// Set up a clean slate for this test. Make sure to reset it after everything
	// below is finished, so we don't accidentally use it elsewhere.
	currentlyRunningTest = newTestInfo()
	defer func() {
		currentlyRunningTest = nil
	}()

	// Run the SetUp function, if any, paying attention to whether it panics.
	setUpPanicked := false
	if tf.SetUp != nil {
		setUpPanicked = runWithProtection(func() { tf.SetUp(currentlyRunningTest) })
	}

	// Run the test function itself, but only if the SetUp function didn't panic.
	// (This includes AssertThat errors.)
	if !setUpPanicked {
		runWithProtection(tf.Run)
	}

	// Run the TearDown function, if any.
	if tf.TearDown != nil {
		runWithProtection(tf.TearDown)
	}

	// Tell the mock controller for the tests to report any errors it's sitting
	// on.
	currentlyRunningTest.MockController.Finish()

	return currentlyRunningTest.failureRecords
}

// Run everything registered with Register (including via the wrapper
// RegisterTestSuite).
//
// Failures are communicated to the supplied testing.T object. This is the
// bridge between ogletest and the testing package (and `go test`); you should
// ensure that it's called at least once by creating a test function compatible
// with `go test` and calling it there.
//
// For example:
//
//     import (
//       "github.com/jacobsa/ogletest"
//       "testing"
//     )
//
//     func TestOgletest(t *testing.T) {
//       ogletest.RunTests(t)
//     }
//
func RunTests(t *testing.T) {
	runTestsOnce.Do(func() { runTestsInternal(t) })
}

// runTestsInternal does the real work of RunTests, which simply wraps it in a
// sync.Once.
func runTestsInternal(t *testing.T) {
	// Process each registered suite.
	for _, suite := range registeredSuites {
		// Stop now if we've already seen a failure and we've been told to stop
		// early.
		if t.Failed() && *fStopEarly {
			break
		}

		// Print a banner.
		fmt.Printf("[----------] Running tests from %s\n", suite.Name)

		// Run the SetUp function, if any.
		if suite.SetUp != nil {
			suite.SetUp()
		}

		// Run each test function that the user has not told us to skip.
		for _, tf := range filterTestFunctions(suite) {
			// Print a banner for the start of this test function.
			fmt.Printf("[ RUN      ] %s.%s\n", suite.Name, tf.Name)

			// Run the test function.
			startTime := time.Now()
			failures := runTestFunction(tf)
			runDuration := time.Since(startTime)

			// Print any failures, and mark the test as having failed if there are any.
			for _, record := range failures {
				t.Fail()
				fmt.Printf(
					"%s:%d:\n%s\n\n",
					record.FileName,
					record.LineNumber,
					record.Error)
			}

			// Print a banner for the end of the test.
			bannerMessage := "[       OK ]"
			if len(failures) != 0 {
				bannerMessage = "[  FAILED  ]"
			}

			// Print a summary of the time taken, if long enough.
			var timeMessage string
			if runDuration >= 25*time.Millisecond {
				timeMessage = fmt.Sprintf(" (%s)", runDuration.String())
			}

			fmt.Printf(
				"%s %s.%s%s\n",
				bannerMessage,
				suite.Name,
				tf.Name,
				timeMessage)

			// Stop running tests from this suite if we've been told to stop early
			// and this test failed.
			if t.Failed() && *fStopEarly {
				break
			}
		}

		// Run the suite's TearDown function, if any.
		if suite.TearDown != nil {
			suite.TearDown()
		}

		fmt.Printf("[----------] Finished with tests from %s\n", suite.Name)
	}
}

// Return true iff the supplied program counter appears to lie within panic().
func isPanic(pc uintptr) bool {
	f := runtime.FuncForPC(pc)
	if f == nil {
		return false
	}

	return f.Name() == "runtime.gopanic" || f.Name() == "runtime.sigpanic"
}

// Find the deepest stack frame containing something that appears to be a
// panic. Return the 'skip' value that a caller to this function would need
// to supply to runtime.Caller for that frame, or a negative number if not found.
func findPanic() int {
	localSkip := -1
	for i := 0; ; i++ {
		// Stop if we've passed the base of the stack.
		pc, _, _, ok := runtime.Caller(i)
		if !ok {
			break
		}

		// Is this a panic?
		if isPanic(pc) {
			localSkip = i
		}
	}

	return localSkip - 1
}

// Attempt to find the file base name and line number for the ultimate source
// of a panic, on the panicking stack. Return a human-readable sentinel if
// unsuccessful.
func findPanicFileLine() (string, int) {
	panicSkip := findPanic()
	if panicSkip < 0 {
		return "(unknown)", 0
	}

	// Find the trigger of the panic.
	_, file, line, ok := runtime.Caller(panicSkip + 1)
	if !ok {
		return "(unknown)", 0
	}

	return path.Base(file), line
}

// Run the supplied function, catching panics (including AssertThat errors) and
// reporting them to the currently-running test as appropriate. Return true iff
// the function panicked.
func runWithProtection(f func()) (panicked bool) {
	defer func() {
		// If the test didn't panic, we're done.
		r := recover()
		if r == nil {
			return
		}

		panicked = true

		// We modify the currently running test below.
		currentlyRunningTest.mu.Lock()
		defer currentlyRunningTest.mu.Unlock()

		// If the function panicked (and the panic was not due to an AssertThat
		// failure), add a failure for the panic.
		if !isAbortError(r) {
			var panicRecord FailureRecord
			panicRecord.FileName, panicRecord.LineNumber = findPanicFileLine()
			panicRecord.Error = fmt.Sprintf(
				"panic: %v\n\n%s", r, formatPanicStack())

			currentlyRunningTest.failureRecords = append(
				currentlyRunningTest.failureRecords,
				panicRecord)
		}
	}()

	f()
	return
}

func formatPanicStack() string {
	buf := new(bytes.Buffer)

	// Find the panic. If successful, we'll skip to below it. Otherwise, we'll
	// format everything.
	var initialSkip int
	if panicSkip := findPanic(); panicSkip >= 0 {
		initialSkip = panicSkip + 1
	}

	for i := initialSkip; ; i++ {
		pc, file, line, ok := runtime.Caller(i)
		if !ok {
			break
		}

		// Choose a function name to display.
		funcName := "(unknown)"
		if f := runtime.FuncForPC(pc); f != nil {
			funcName = f.Name()
		}

		// Stop if we've gotten as far as the test runner code.
		if funcName == "github.com/jacobsa/ogletest.runTestMethod" ||
			funcName == "github.com/jacobsa/ogletest.runWithProtection" {
			break
		}

		// Add an entry for this frame.
		fmt.Fprintf(buf, "%s\n\t%s:%d\n", funcName, file, line)
	}

	return buf.String()
}

// Filter test functions according to the user-supplied filter flag.
func filterTestFunctions(suite TestSuite) (out []TestFunction) {
	re, err := regexp.Compile(*fTestFilter)
	if err != nil {
		panic("Invalid value for --ogletest.run: " + err.Error())
	}

	for _, tf := range suite.TestFunctions {
		fullName := fmt.Sprintf("%s.%s", suite.Name, tf.Name)
		if !re.MatchString(fullName) {
			continue
		}

		out = append(out, tf)
	}

	return
}
<|endoftext|>


<commit_before>// Copyright 2018 The Nakama 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 server

import (
	"context"
	"database/sql"
	"fmt"
	"net"
	"net/http"
	"time"

	"github.com/gorilla/handlers"
	"github.com/gorilla/mux"
	"github.com/grpc-ecosystem/grpc-gateway/runtime"
	"github.com/heroiclabs/nakama/console"
	"go.opencensus.io/plugin/ocgrpc"
	"go.uber.org/zap"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/metadata"
	"google.golang.org/grpc/status"
)

type ConsoleServer struct {
	logger            *zap.Logger
	db                *sql.DB
	config            Config
	grpcServer        *grpc.Server
	grpcGatewayServer *http.Server
}

func StartConsoleServer(logger *zap.Logger, startupLogger *zap.Logger, config Config, db *sql.DB) *ConsoleServer {
	serverOpts := []grpc.ServerOption{
		grpc.StatsHandler(&ocgrpc.ServerHandler{IsPublicEndpoint: true}),
		grpc.MaxRecvMsgSize(int(config.GetSocket().MaxMessageSizeBytes)),
		grpc.UnaryInterceptor(consoleInterceptorFunc(logger, config)),
	}
	grpcServer := grpc.NewServer(serverOpts...)

	s := &ConsoleServer{
		logger:     logger,
		db:         db,
		config:     config,
		grpcServer: grpcServer,
	}

	console.RegisterConsoleServer(grpcServer, s)
	startupLogger.Info("Starting Console server for gRPC requests", zap.Int("port", config.GetSocket().Port-2))
	go func() {
		listener, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", config.GetSocket().Port-2))
		if err != nil {
			startupLogger.Fatal("Console server listener failed to start", zap.Error(err))
		}

		if err := grpcServer.Serve(listener); err != nil {
			startupLogger.Fatal("Console server listener failed", zap.Error(err))
		}
	}()

	ctx := context.Background()
	grpcGateway := runtime.NewServeMux()
	dialAddr := fmt.Sprintf("127.0.0.1:%d", config.GetSocket().Port-2)
	dialOpts := []grpc.DialOption{
		//TODO (mo, zyro): Do we need to pass the statsHandler here as well?
		grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(int(config.GetSocket().MaxMessageSizeBytes))),
		grpc.WithInsecure(),
	}

	if err := console.RegisterConsoleHandlerFromEndpoint(ctx, grpcGateway, dialAddr, dialOpts); err != nil {
		startupLogger.Fatal("Console server gateway registration failed", zap.Error(err))
	}

	grpcGatewayRouter := mux.NewRouter()
	grpcGatewayRouter.NewRoute().Handler(grpcGateway)
	//TODO server HTML content here.
	grpcGatewayRouter.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(200) }).Methods("GET")
	// Enable compression on gateway responses.
	handlerWithGzip := handlers.CompressHandler(grpcGatewayRouter)

	// Enable CORS on all requests.
	CORSHeaders := handlers.AllowedHeaders([]string{"Authorization", "Content-Type", "User-Agent"})
	CORSOrigins := handlers.AllowedOrigins([]string{"*"})
	CORSMethods := handlers.AllowedMethods([]string{"GET", "HEAD", "POST", "PUT", "DELETE", "PATCH"})
	handlerWithCORS := handlers.CORS(CORSHeaders, CORSOrigins, CORSMethods)(handlerWithGzip)

	// Set up and start GRPC Gateway server.
	s.grpcGatewayServer = &http.Server{
		Addr:         fmt.Sprintf(":%d", config.GetConsole().Port),
		ReadTimeout:  time.Millisecond * time.Duration(int64(config.GetSocket().ReadTimeoutMs)),
		WriteTimeout: time.Millisecond * time.Duration(int64(config.GetSocket().WriteTimeoutMs)),
		IdleTimeout:  time.Millisecond * time.Duration(int64(config.GetSocket().IdleTimeoutMs)),
		Handler:      handlerWithCORS,
	}

	startupLogger.Info("Starting Console server gateway for HTTP requests", zap.Int("port", config.GetConsole().Port))
	go func() {
		if err := s.grpcGatewayServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
			startupLogger.Fatal("Console server gateway listener failed", zap.Error(err))
		}
	}()

	return s
}

func (s *ConsoleServer) Stop() {
	// 1. Stop GRPC Gateway server first as it sits above GRPC server.
	if err := s.grpcGatewayServer.Shutdown(context.Background()); err != nil {
		s.logger.Error("API server gateway listener shutdown failed", zap.Error(err))
	}
	// 2. Stop GRPC server.
	s.grpcServer.GracefulStop()
}

func consoleInterceptorFunc(logger *zap.Logger, config Config) func(context.Context, interface{}, *grpc.UnaryServerInfo, grpc.UnaryHandler) (interface{}, error) {
	return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {

		md, ok := metadata.FromIncomingContext(ctx)
		if !ok {
			logger.Error("Cannot extract metadata from incoming context")
			return nil, status.Error(codes.FailedPrecondition, "Cannot extract metadata from incoming context")
		}
		auth, ok := md["authorization"]
		if !ok {
			auth, ok = md["grpcgateway-authorization"]
		}
		if !ok {
			return nil, status.Error(codes.Unauthenticated, "Console authentication required.")
		}
		if len(auth) != 1 {
			return nil, status.Error(codes.Unauthenticated, "Console authentication required.")
		}
		username, password, ok := parseBasicAuth(auth[0])
		if !ok {
			return nil, status.Error(codes.Unauthenticated, "Console authentication invalid.")
		}
		if username != config.GetConsole().Username || password != config.GetConsole().Password {
			return nil, status.Error(codes.Unauthenticated, "Console authentication invalid.")
		}

		return handler(ctx, req)
	}
}

func (s *ConsoleServer) Login(context.Context, *console.AuthenticateRequest) (*console.Session, error) {
	return nil, nil
}
<commit_msg>Improve console gateway router setup<commit_after>// Copyright 2018 The Nakama 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 server

import (
	"context"
	"database/sql"
	"fmt"
	"net"
	"net/http"
	"time"

	"github.com/gorilla/handlers"
	"github.com/gorilla/mux"
	"github.com/grpc-ecosystem/grpc-gateway/runtime"
	"github.com/heroiclabs/nakama/console"
	"go.opencensus.io/plugin/ocgrpc"
	"go.uber.org/zap"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/metadata"
	"google.golang.org/grpc/status"
)

type ConsoleServer struct {
	logger            *zap.Logger
	db                *sql.DB
	config            Config
	grpcServer        *grpc.Server
	grpcGatewayServer *http.Server
}

func StartConsoleServer(logger *zap.Logger, startupLogger *zap.Logger, config Config, db *sql.DB) *ConsoleServer {
	serverOpts := []grpc.ServerOption{
		grpc.StatsHandler(&ocgrpc.ServerHandler{IsPublicEndpoint: true}),
		grpc.MaxRecvMsgSize(int(config.GetSocket().MaxMessageSizeBytes)),
		grpc.UnaryInterceptor(consoleInterceptorFunc(logger, config)),
	}
	grpcServer := grpc.NewServer(serverOpts...)

	s := &ConsoleServer{
		logger:     logger,
		db:         db,
		config:     config,
		grpcServer: grpcServer,
	}

	console.RegisterConsoleServer(grpcServer, s)
	startupLogger.Info("Starting Console server for gRPC requests", zap.Int("port", config.GetSocket().Port-2))
	go func() {
		listener, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", config.GetSocket().Port-2))
		if err != nil {
			startupLogger.Fatal("Console server listener failed to start", zap.Error(err))
		}

		if err := grpcServer.Serve(listener); err != nil {
			startupLogger.Fatal("Console server listener failed", zap.Error(err))
		}
	}()

	ctx := context.Background()
	grpcGateway := runtime.NewServeMux()
	dialAddr := fmt.Sprintf("127.0.0.1:%d", config.GetSocket().Port-2)
	dialOpts := []grpc.DialOption{
		//TODO (mo, zyro): Do we need to pass the statsHandler here as well?
		grpc.WithDefaultCallOptions(grpc.MaxCallSendMsgSize(int(config.GetSocket().MaxMessageSizeBytes))),
		grpc.WithInsecure(),
	}

	if err := console.RegisterConsoleHandlerFromEndpoint(ctx, grpcGateway, dialAddr, dialOpts); err != nil {
		startupLogger.Fatal("Console server gateway registration failed", zap.Error(err))
	}

	grpcGatewayRouter := mux.NewRouter()
	//TODO server HTML content here.
	grpcGatewayRouter.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(200) }).Methods("GET")
	// Enable compression on gateway responses.
	handlerWithGzip := handlers.CompressHandler(grpcGateway)
	grpcGatewayRouter.NewRoute().Handler(handlerWithGzip)

	// Enable CORS on all requests.
	CORSHeaders := handlers.AllowedHeaders([]string{"Authorization", "Content-Type", "User-Agent"})
	CORSOrigins := handlers.AllowedOrigins([]string{"*"})
	CORSMethods := handlers.AllowedMethods([]string{"GET", "HEAD", "POST", "PUT", "DELETE", "PATCH"})
	handlerWithCORS := handlers.CORS(CORSHeaders, CORSOrigins, CORSMethods)(grpcGatewayRouter)

	// Set up and start GRPC Gateway server.
	s.grpcGatewayServer = &http.Server{
		Addr:         fmt.Sprintf(":%d", config.GetConsole().Port),
		ReadTimeout:  time.Millisecond * time.Duration(int64(config.GetSocket().ReadTimeoutMs)),
		WriteTimeout: time.Millisecond * time.Duration(int64(config.GetSocket().WriteTimeoutMs)),
		IdleTimeout:  time.Millisecond * time.Duration(int64(config.GetSocket().IdleTimeoutMs)),
		Handler:      handlerWithCORS,
	}

	startupLogger.Info("Starting Console server gateway for HTTP requests", zap.Int("port", config.GetConsole().Port))
	go func() {
		if err := s.grpcGatewayServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
			startupLogger.Fatal("Console server gateway listener failed", zap.Error(err))
		}
	}()

	return s
}

func (s *ConsoleServer) Stop() {
	// 1. Stop GRPC Gateway server first as it sits above GRPC server.
	if err := s.grpcGatewayServer.Shutdown(context.Background()); err != nil {
		s.logger.Error("API server gateway listener shutdown failed", zap.Error(err))
	}
	// 2. Stop GRPC server.
	s.grpcServer.GracefulStop()
}

func consoleInterceptorFunc(logger *zap.Logger, config Config) func(context.Context, interface{}, *grpc.UnaryServerInfo, grpc.UnaryHandler) (interface{}, error) {
	return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {

		md, ok := metadata.FromIncomingContext(ctx)
		if !ok {
			logger.Error("Cannot extract metadata from incoming context")
			return nil, status.Error(codes.FailedPrecondition, "Cannot extract metadata from incoming context")
		}
		auth, ok := md["authorization"]
		if !ok {
			auth, ok = md["grpcgateway-authorization"]
		}
		if !ok {
			return nil, status.Error(codes.Unauthenticated, "Console authentication required.")
		}
		if len(auth) != 1 {
			return nil, status.Error(codes.Unauthenticated, "Console authentication required.")
		}
		username, password, ok := parseBasicAuth(auth[0])
		if !ok {
			return nil, status.Error(codes.Unauthenticated, "Console authentication invalid.")
		}
		if username != config.GetConsole().Username || password != config.GetConsole().Password {
			return nil, status.Error(codes.Unauthenticated, "Console authentication invalid.")
		}

		return handler(ctx, req)
	}
}

func (s *ConsoleServer) Login(context.Context, *console.AuthenticateRequest) (*console.Session, error) {
	return nil, nil
}
<|endoftext|>


<commit_before>package server

import (
	"encoding/json"
	"fmt"
	"net/http"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/jingweno/jqplay/config"
	"github.com/jingweno/jqplay/jq"
	"golang.org/x/net/context"
	"gopkg.in/gin-gonic/gin.v1"
)

const (
	JSONPayloadLimit   = JSONPayloadLimitMB * OneMB
	JSONPayloadLimitMB = 10
	OneMB              = 1024000
)

type JQHandlerContext struct {
	*config.Config
	JQ string
}

func (c *JQHandlerContext) Asset(path string) string {
	return fmt.Sprintf("%s/%s", c.AssetHost, path)
}

func (c *JQHandlerContext) ShouldInitJQ() bool {
	return c.JQ != ""
}

type JQHandler struct {
	Config *config.Config
}

func (h *JQHandler) handleIndex(c *gin.Context) {
	c.HTML(200, "index.tmpl", &JQHandlerContext{Config: h.Config})
}

func (h *JQHandler) handleJqPost(c *gin.Context) {
	l, _ := c.Get("logger")
	logger := l.(*logrus.Entry)

	if c.Request.ContentLength > JSONPayloadLimit {
		size := float64(c.Request.ContentLength) / OneMB
		err := fmt.Errorf("JSON payload size is %.1fMB, larger than limit %dMB.", size, JSONPayloadLimitMB)
		logger.WithError(err).WithField("size", size).Infof(err.Error())
		c.String(http.StatusExpectationFailed, err.Error())
		return
	}

	ctx, cancel := context.WithTimeout(c.Request.Context(), 3*time.Second)
	defer cancel()

	c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, JSONPayloadLimit)

	var jq *jq.JQ
	err := c.BindJSON(&jq)
	if err != nil {
		err = fmt.Errorf("error parsing JSON: %s", err)
		logger.WithError(err).Infof("error parsing JSON: %s", err)
		c.String(422, err.Error())
		return
	}

	// Evaling into ResponseWriter sets the status code to 200
	// appending error message in the end if there's any
	err = jq.Eval(ctx, c.Writer)
	if err != nil {
		fmt.Fprint(c.Writer, err.Error())
	}
}

func (h *JQHandler) handleJqGet(c *gin.Context) {
	jq := &jq.JQ{
		J: c.Query("j"),
		Q: c.Query("q"),
	}

	var jqData string
	if err := jq.Validate(); err == nil {
		d, err := json.Marshal(jq)
		if err == nil {
			jqData = string(d)
		}
	}

	c.HTML(200, "index.tmpl", &JQHandlerContext{Config: h.Config, JQ: jqData})
}
<commit_msg>Log err<commit_after>package server

import (
	"encoding/json"
	"fmt"
	"net/http"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/jingweno/jqplay/config"
	"github.com/jingweno/jqplay/jq"
	"golang.org/x/net/context"
	"gopkg.in/gin-gonic/gin.v1"
)

const (
	JSONPayloadLimit   = JSONPayloadLimitMB * OneMB
	JSONPayloadLimitMB = 10
	OneMB              = 1024000
)

type JQHandlerContext struct {
	*config.Config
	JQ string
}

func (c *JQHandlerContext) Asset(path string) string {
	return fmt.Sprintf("%s/%s", c.AssetHost, path)
}

func (c *JQHandlerContext) ShouldInitJQ() bool {
	return c.JQ != ""
}

type JQHandler struct {
	Config *config.Config
}

func (h *JQHandler) handleIndex(c *gin.Context) {
	c.HTML(200, "index.tmpl", &JQHandlerContext{Config: h.Config})
}

func (h *JQHandler) handleJqPost(c *gin.Context) {
	l, _ := c.Get("logger")
	logger := l.(*logrus.Entry)

	if c.Request.ContentLength > JSONPayloadLimit {
		size := float64(c.Request.ContentLength) / OneMB
		err := fmt.Errorf("JSON payload size is %.1fMB, larger than limit %dMB.", size, JSONPayloadLimitMB)
		logger.WithError(err).WithField("size", size).Infof(err.Error())
		c.String(http.StatusExpectationFailed, err.Error())
		return
	}

	ctx, cancel := context.WithTimeout(c.Request.Context(), 3*time.Second)
	defer cancel()

	c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, JSONPayloadLimit)

	var jq *jq.JQ
	err := c.BindJSON(&jq)
	if err != nil {
		err = fmt.Errorf("error parsing JSON: %s", err)
		logger.WithError(err).Infof("error parsing JSON: %s", err)
		c.String(422, err.Error())
		return
	}

	// Evaling into ResponseWriter sets the status code to 200
	// appending error message in the end if there's any
	err = jq.Eval(ctx, c.Writer)
	if err != nil {
		logger.WithError(err).Infof("jq error: %s", err)
		fmt.Fprint(c.Writer, err.Error())
	}
}

func (h *JQHandler) handleJqGet(c *gin.Context) {
	jq := &jq.JQ{
		J: c.Query("j"),
		Q: c.Query("q"),
	}

	var jqData string
	if err := jq.Validate(); err == nil {
		d, err := json.Marshal(jq)
		if err == nil {
			jqData = string(d)
		}
	}

	c.HTML(200, "index.tmpl", &JQHandlerContext{Config: h.Config, JQ: jqData})
}
<|endoftext|>


<commit_before>package main

import (
	"strings"

	"github.com/docker/docker/pkg/integration/checker"
	"github.com/docker/docker/pkg/stringid"
	"github.com/go-check/check"
)

func (s *DockerSuite) TestRenameStoppedContainer(c *check.C) {
	testRequires(c, DaemonIsLinux)
	out, _ := dockerCmd(c, "run", "--name", "first_name", "-d", "busybox", "sh")

	cleanedContainerID := strings.TrimSpace(out)
	dockerCmd(c, "wait", cleanedContainerID)

	name, err := inspectField(cleanedContainerID, "Name")
	newName := "new_name" + stringid.GenerateNonCryptoID()
	dockerCmd(c, "rename", "first_name", newName)

	name, err = inspectField(cleanedContainerID, "Name")
	c.Assert(err, checker.IsNil, check.Commentf("Failed to rename container %s", name))
	c.Assert(name, checker.Equals, "/"+newName, check.Commentf("Failed to rename container %s", name))

}

func (s *DockerSuite) TestRenameRunningContainer(c *check.C) {
	testRequires(c, DaemonIsLinux)
	out, _ := dockerCmd(c, "run", "--name", "first_name", "-d", "busybox", "sh")

	newName := "new_name" + stringid.GenerateNonCryptoID()
	cleanedContainerID := strings.TrimSpace(out)
	dockerCmd(c, "rename", "first_name", newName)

	name, err := inspectField(cleanedContainerID, "Name")
	c.Assert(err, checker.IsNil, check.Commentf("Failed to rename container %s", name))
	c.Assert(name, checker.Equals, "/"+newName, check.Commentf("Failed to rename container %s", name))
}

func (s *DockerSuite) TestRenameCheckNames(c *check.C) {
	testRequires(c, DaemonIsLinux)
	dockerCmd(c, "run", "--name", "first_name", "-d", "busybox", "sh")

	newName := "new_name" + stringid.GenerateNonCryptoID()
	dockerCmd(c, "rename", "first_name", newName)

	name, err := inspectField(newName, "Name")
	c.Assert(err, checker.IsNil, check.Commentf("Failed to rename container %s", name))
	c.Assert(name, checker.Equals, "/"+newName, check.Commentf("Failed to rename container %s", name))

	name, err = inspectField("first_name", "Name")
	c.Assert(err, checker.NotNil, check.Commentf(name))
	c.Assert(err.Error(), checker.Contains, "No such image or container: first_name")
}

func (s *DockerSuite) TestRenameInvalidName(c *check.C) {
	testRequires(c, DaemonIsLinux)
	dockerCmd(c, "run", "--name", "myname", "-d", "busybox", "top")

	out, _, err := dockerCmdWithError("rename", "myname", "new:invalid")
	c.Assert(err, checker.NotNil, check.Commentf("Renaming container to invalid name should have failed: %s", out))
	c.Assert(out, checker.Contains, "Invalid container name", check.Commentf("%v", err))

	out, _, err = dockerCmdWithError("rename", "myname", "")
	c.Assert(err, checker.NotNil, check.Commentf("Renaming container to invalid name should have failed: %s", out))
	c.Assert(out, checker.Contains, "may be empty", check.Commentf("%v", err))

	out, _, err = dockerCmdWithError("rename", "", "newname")
	c.Assert(err, checker.NotNil, check.Commentf("Renaming container with empty name should have failed: %s", out))
	c.Assert(out, checker.Contains, "may be empty", check.Commentf("%v", err))

	out, _ = dockerCmd(c, "ps", "-a")
	c.Assert(out, checker.Contains, "myname", check.Commentf("Output of docker ps should have included 'myname': %s", out))
}
<commit_msg>integration-cli test for active container rename and reuse<commit_after>package main

import (
	"strings"

	"github.com/docker/docker/pkg/integration/checker"
	"github.com/docker/docker/pkg/stringid"
	"github.com/go-check/check"
)

func (s *DockerSuite) TestRenameStoppedContainer(c *check.C) {
	testRequires(c, DaemonIsLinux)
	out, _ := dockerCmd(c, "run", "--name", "first_name", "-d", "busybox", "sh")

	cleanedContainerID := strings.TrimSpace(out)
	dockerCmd(c, "wait", cleanedContainerID)

	name, err := inspectField(cleanedContainerID, "Name")
	newName := "new_name" + stringid.GenerateNonCryptoID()
	dockerCmd(c, "rename", "first_name", newName)

	name, err = inspectField(cleanedContainerID, "Name")
	c.Assert(err, checker.IsNil, check.Commentf("Failed to rename container %s", name))
	c.Assert(name, checker.Equals, "/"+newName, check.Commentf("Failed to rename container %s", name))

}

func (s *DockerSuite) TestRenameRunningContainer(c *check.C) {
	testRequires(c, DaemonIsLinux)
	out, _ := dockerCmd(c, "run", "--name", "first_name", "-d", "busybox", "sh")

	newName := "new_name" + stringid.GenerateNonCryptoID()
	cleanedContainerID := strings.TrimSpace(out)
	dockerCmd(c, "rename", "first_name", newName)

	name, err := inspectField(cleanedContainerID, "Name")
	c.Assert(err, checker.IsNil, check.Commentf("Failed to rename container %s", name))
	c.Assert(name, checker.Equals, "/"+newName, check.Commentf("Failed to rename container %s", name))
}

func (s *DockerSuite) TestRenameRunningContainerAndReuse(c *check.C) {
	testRequires(c, DaemonIsLinux)
	out, _ := dockerCmd(c, "run", "--name", "first_name", "-d", "busybox", "top")
	c.Assert(waitRun("first_name"), check.IsNil)

	newName := "new_name"
	ContainerID := strings.TrimSpace(out)
	dockerCmd(c, "rename", "first_name", newName)

	name, err := inspectField(ContainerID, "Name")
	c.Assert(err, checker.IsNil, check.Commentf("Failed to rename container %s", name))
	c.Assert(name, checker.Equals, "/"+newName, check.Commentf("Failed to rename container"))

	out, _ = dockerCmd(c, "run", "--name", "first_name", "-d", "busybox", "top")
	c.Assert(waitRun("first_name"), check.IsNil)
	newContainerID := strings.TrimSpace(out)
	name, err = inspectField(newContainerID, "Name")
	c.Assert(err, checker.IsNil, check.Commentf("Failed to reuse container name"))
	c.Assert(name, checker.Equals, "/first_name", check.Commentf("Failed to reuse container name"))
}

func (s *DockerSuite) TestRenameCheckNames(c *check.C) {
	testRequires(c, DaemonIsLinux)
	dockerCmd(c, "run", "--name", "first_name", "-d", "busybox", "sh")

	newName := "new_name" + stringid.GenerateNonCryptoID()
	dockerCmd(c, "rename", "first_name", newName)

	name, err := inspectField(newName, "Name")
	c.Assert(err, checker.IsNil, check.Commentf("Failed to rename container %s", name))
	c.Assert(name, checker.Equals, "/"+newName, check.Commentf("Failed to rename container %s", name))

	name, err = inspectField("first_name", "Name")
	c.Assert(err, checker.NotNil, check.Commentf(name))
	c.Assert(err.Error(), checker.Contains, "No such image or container: first_name")
}

func (s *DockerSuite) TestRenameInvalidName(c *check.C) {
	testRequires(c, DaemonIsLinux)
	dockerCmd(c, "run", "--name", "myname", "-d", "busybox", "top")

	out, _, err := dockerCmdWithError("rename", "myname", "new:invalid")
	c.Assert(err, checker.NotNil, check.Commentf("Renaming container to invalid name should have failed: %s", out))
	c.Assert(out, checker.Contains, "Invalid container name", check.Commentf("%v", err))

	out, _, err = dockerCmdWithError("rename", "myname", "")
	c.Assert(err, checker.NotNil, check.Commentf("Renaming container to invalid name should have failed: %s", out))
	c.Assert(out, checker.Contains, "may be empty", check.Commentf("%v", err))

	out, _, err = dockerCmdWithError("rename", "", "newname")
	c.Assert(err, checker.NotNil, check.Commentf("Renaming container with empty name should have failed: %s", out))
	c.Assert(out, checker.Contains, "may be empty", check.Commentf("%v", err))

	out, _ = dockerCmd(c, "ps", "-a")
	c.Assert(out, checker.Contains, "myname", check.Commentf("Output of docker ps should have included 'myname': %s", out))
}
<|endoftext|>


<commit_before>/*
Copyright 2015 The Perkeep 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.
*/

// This file adds the "hook" subcommand to devcam, to install and run git hooks.
package main

import (
	"bytes"
	"crypto/rand"
	"errors"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"sort"
	"strings"

	"perkeep.org/pkg/cmdmain"
)

var hookPath = ".git/hooks/"
var hookFiles = []string{
	"pre-commit",
	"commit-msg",
}

var ignoreBelow = []byte("\n# ------------------------ >8 ------------------------\n")

func (c *hookCmd) installHook() error {
	root, err := repoRoot()
	if err != nil {
		return err
	}
	for _, hookFile := range hookFiles {
		filename := filepath.Join(root, hookPath+hookFile)
		hookContent := fmt.Sprintf(hookScript, hookFile)
		// If hook file exists, assume it is okay.
		_, err := os.Stat(filename)
		if err == nil {
			if c.verbose {
				data, err := ioutil.ReadFile(filename)
				if err != nil {
					c.verbosef("reading hook: %v", err)
				} else if string(data) != hookContent {
					c.verbosef("unexpected hook content in %s", filename)
				}
			}
			continue
		}

		if !os.IsNotExist(err) {
			return fmt.Errorf("checking hook: %v", err)
		}
		c.verbosef("installing %s hook", hookFile)
		if err := ioutil.WriteFile(filename, []byte(hookContent), 0700); err != nil {
			return fmt.Errorf("writing hook: %v", err)
		}
	}
	return nil
}

var hookScript = `#!/bin/sh
exec devcam hook %s "$@"
`

type hookCmd struct {
	verbose bool
}

func init() {
	cmdmain.RegisterMode("hook", func(flags *flag.FlagSet) cmdmain.CommandRunner {
		cmd := &hookCmd{}
		flags.BoolVar(&cmd.verbose, "verbose", false, "Be verbose.")
		// TODO(mpl): "-w" flag to run gofmt -w and devcam fixv -w. for now just print instruction.
		return cmd
	})
}

func (c *hookCmd) Usage() {
	printf("Usage: devcam [globalopts] hook [[hook-name] [args...]]\n")
}

func (c *hookCmd) Examples() []string {
	return []string{
		"# install the hooks (if needed)",
		"pre-commit # install the hooks (if needed), then run the pre-commit hook",
	}
}

func (c *hookCmd) Describe() string {
	return "Install git hooks for Perkeep, and if given, run the hook given as argument. Currently available hooks are: " + strings.TrimSuffix(strings.Join(hookFiles, ", "), ",") + "."
}

func (c *hookCmd) RunCommand(args []string) error {
	if err := c.installHook(); err != nil {
		return err
	}
	if len(args) == 0 {
		return nil
	}
	switch args[0] {
	case "pre-commit":
		if err := c.hookPreCommit(args[1:]); err != nil {
			if !(len(args) > 1 && args[1] == "test") {
				printf("You can override these checks with 'git commit --no-verify'\n")
			}
			cmdmain.ExitWithFailure = true
			return err
		}
	case "commit-msg":
		if err := c.hookCommitMsg(args[1:]); err != nil {
			cmdmain.ExitWithFailure = true
			return err
		}
	}
	return nil
}

// stripComments strips lines that begin with "#" and removes the diff section
// contained in verbose commits.
func stripComments(in []byte) []byte {
	if i := bytes.Index(in, ignoreBelow); i >= 0 {
		in = in[:i+1]
	}
	return regexp.MustCompile(`(?m)^#.*\n`).ReplaceAll(in, nil)
}

// hookCommitMsg is installed as the git commit-msg hook.
// It adds a Change-Id line to the bottom of the commit message
// if there is not one already.
// Code mostly copied from golang.org/x/review/git-codereview/hook.go
func (c *hookCmd) hookCommitMsg(args []string) error {
	if len(args) != 1 {
		return errors.New("usage: devcam hook commit-msg message.txt")
	}

	file := args[0]
	oldData, err := ioutil.ReadFile(file)
	if err != nil {
		return err
	}
	data := append([]byte{}, oldData...)
	data = stripComments(data)

	// Empty message not allowed.
	if len(bytes.TrimSpace(data)) == 0 {
		return errors.New("empty commit message")
	}

	// Insert a blank line between first line and subsequent lines if not present.
	eol := bytes.IndexByte(data, '\n')
	if eol != -1 && len(data) > eol+1 && data[eol+1] != '\n' {
		data = append(data, 0)
		copy(data[eol+1:], data[eol:])
		data[eol+1] = '\n'
	}

	// Complain if two Change-Ids are present.
	// This can happen during an interactive rebase;
	// it is easy to forget to remove one of them.
	nChangeId := bytes.Count(data, []byte("\nChange-Id: "))
	if nChangeId > 1 {
		return errors.New("multiple Change-Id lines")
	}

	// Add Change-Id to commit message if not present.
	if nChangeId == 0 {
		n := len(data)
		for n > 0 && data[n-1] == '\n' {
			n--
		}
		var id [20]byte
		if _, err := io.ReadFull(rand.Reader, id[:]); err != nil {
			return fmt.Errorf("could not generate Change-Id: %v", err)
		}
		data = append(data[:n], fmt.Sprintf("\n\nChange-Id: I%x\n", id[:])...)
	}

	// Write back.
	if !bytes.Equal(data, oldData) {
		return ioutil.WriteFile(file, data, 0666)
	}
	return nil
}

// hookPreCommit does the following checks, in order:
// gofmt, and trailing space.
// If appropriate, any one of these checks prints the action
// required from the user, and the following checks are not
// performed.
func (c *hookCmd) hookPreCommit(args []string) (err error) {
	if err = c.hookGofmt(); err != nil {
		return err
	}
	return c.hookTrailingSpace()
}

// hookGofmt runs a gofmt check on the local files matching the files in the
// git staging area.
// An error is returned if something went wrong or if some of the files need
// gofmting. In the latter case, the instruction is printed.
func (c *hookCmd) hookGofmt() error {
	if os.Getenv("GIT_GOFMT_HOOK") == "off" {
		printf("gofmt disabled by $GIT_GOFMT_HOOK=off\n")
		return nil
	}

	files, err := c.runGofmt()
	if err != nil {
		printf("gofmt hook reported errors:\n\t%v\n", strings.Replace(strings.TrimSpace(err.Error()), "\n", "\n\t", -1))
		return errors.New("gofmt errors")
	}
	if len(files) == 0 {
		return nil
	}
	printf("You need to format with gofmt:\n\tgofmt -w %s\n",
		strings.Join(files, " "))
	return errors.New("gofmt required")
}

func (c *hookCmd) hookTrailingSpace() error {
	// see 'pathspec' for the ':!' syntax to ignore a directory.
	out, _ := cmdOutputDirErr(".", "git", "diff-index", "--check", "--diff-filter=ACM", "--cached", "HEAD", "--", ".", ":!/vendor/")
	if out != "" {
		printf("\n%s", out)
		printf("Trailing whitespace detected, you need to clean it up manually.\n")
		return errors.New("trailing whitespace")
	}
	return nil
}

// runGofmt runs the external gofmt command over the local version of staged files.
// It returns the files that need gofmting.
func (c *hookCmd) runGofmt() (files []string, err error) {
	repo, err := repoRoot()
	if err != nil {
		return nil, err
	}
	if !strings.HasSuffix(repo, string(filepath.Separator)) {
		repo += string(filepath.Separator)
	}

	out, err := cmdOutputDirErr(".", "git", "diff-index", "--name-only", "--diff-filter=ACM", "--cached", "HEAD", "--", ":(glob)**/*.go", ":!/vendor/")
	if err != nil {
		return nil, err
	}
	indexFiles := addRoot(repo, nonBlankLines(out))
	if len(indexFiles) == 0 {
		return
	}

	args := []string{"-l"}
	// TODO(mpl): it would be nice to TrimPrefix the pwd from each file to get a shorter output.
	// However, since git sets the pwd to GIT_DIR before running the pre-commit hook, we lost
	// the actual pwd from when we ran `git commit`, so no dice so far.
	for _, file := range indexFiles {
		args = append(args, file)
	}

	if c.verbose {
		fmt.Fprintln(cmdmain.Stderr, commandString("gofmt", args))
	}
	cmd := exec.Command("gofmt", args...)
	var stdout, stderr bytes.Buffer
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr
	err = cmd.Run()

	if err != nil {
		// Error but no stderr: usually can't find gofmt.
		if stderr.Len() == 0 {
			return nil, fmt.Errorf("invoking gofmt: %v", err)
		}
		return nil, fmt.Errorf("%s: %v", stderr.String(), err)
	}

	// Build file list.
	files = lines(stdout.String())
	sort.Strings(files)
	return files, nil
}

func printf(format string, args ...interface{}) {
	cmdmain.Errorf(format, args...)
}

func addRoot(root string, list []string) []string {
	var out []string
	for _, x := range list {
		out = append(out, filepath.Join(root, x))
	}
	return out
}

// nonBlankLines returns the non-blank lines in text.
func nonBlankLines(text string) []string {
	var out []string
	for _, s := range lines(text) {
		if strings.TrimSpace(s) != "" {
			out = append(out, s)
		}
	}
	return out
}

// filter returns the elements in list satisfying f.
func filter(f func(string) bool, list []string) []string {
	var out []string
	for _, x := range list {
		if f(x) {
			out = append(out, x)
		}
	}
	return out
}

func commandString(command string, args []string) string {
	return strings.Join(append([]string{command}, args...), " ")
}

func lines(text string) []string {
	out := strings.Split(text, "\n")
	// Split will include a "" after the last line. Remove it.
	if n := len(out) - 1; n >= 0 && out[n] == "" {
		out = out[:n]
	}
	return out
}

func (c *hookCmd) verbosef(format string, args ...interface{}) {
	if c.verbose {
		fmt.Fprintf(cmdmain.Stdout, format, args...)
	}
}

// cmdOutputDirErr runs the command line in dir, returning its output
// and any error results.
//
// NOTE: cmdOutputDirErr must be used only to run commands that read state,
// not for commands that make changes. Commands that make changes
// should be run using runDirErr so that the -v and -n flags apply to them.
func cmdOutputDirErr(dir, command string, args ...string) (string, error) {
	// NOTE: We only show these non-state-modifying commands with -v -v.
	// Otherwise things like 'git sync -v' show all our internal "find out about
	// the git repo" commands, which is confusing if you are just trying to find
	// out what git sync means.

	cmd := exec.Command(command, args...)
	if dir != "." {
		cmd.Dir = dir
	}
	b, err := cmd.CombinedOutput()
	return string(b), err
}
<commit_msg>devcam: create hooks dir if it does not exist<commit_after>/*
Copyright 2015 The Perkeep 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.
*/

// This file adds the "hook" subcommand to devcam, to install and run git hooks.
package main

import (
	"bytes"
	"crypto/rand"
	"errors"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"sort"
	"strings"

	"perkeep.org/pkg/cmdmain"
)

var hookPath = ".git/hooks/"
var hookFiles = []string{
	"pre-commit",
	"commit-msg",
}

var ignoreBelow = []byte("\n# ------------------------ >8 ------------------------\n")

func (c *hookCmd) installHook() error {
	root, err := repoRoot()
	if err != nil {
		return err
	}
	hookDir := filepath.Join(root, hookPath)
	if _, err := os.Stat(hookDir); err != nil {
		if !os.IsNotExist(err) {
			return err
		}
		if err := os.MkdirAll(hookDir, 0700); err != nil {
			return err
		}
	}
	for _, hookFile := range hookFiles {
		filename := hookDir + hookFile
		hookContent := fmt.Sprintf(hookScript, hookFile)
		// If hook file exists, assume it is okay.
		_, err := os.Stat(filename)
		if err == nil {
			if c.verbose {
				data, err := ioutil.ReadFile(filename)
				if err != nil {
					c.verbosef("reading hook: %v", err)
				} else if string(data) != hookContent {
					c.verbosef("unexpected hook content in %s", filename)
				}
			}
			continue
		}

		if !os.IsNotExist(err) {
			return fmt.Errorf("checking hook: %v", err)
		}
		c.verbosef("installing %s hook", hookFile)
		if err := ioutil.WriteFile(filename, []byte(hookContent), 0700); err != nil {
			return fmt.Errorf("writing hook: %v", err)
		}
	}
	return nil
}

var hookScript = `#!/bin/sh
exec devcam hook %s "$@"
`

type hookCmd struct {
	verbose bool
}

func init() {
	cmdmain.RegisterMode("hook", func(flags *flag.FlagSet) cmdmain.CommandRunner {
		cmd := &hookCmd{}
		flags.BoolVar(&cmd.verbose, "verbose", false, "Be verbose.")
		// TODO(mpl): "-w" flag to run gofmt -w and devcam fixv -w. for now just print instruction.
		return cmd
	})
}

func (c *hookCmd) Usage() {
	printf("Usage: devcam [globalopts] hook [[hook-name] [args...]]\n")
}

func (c *hookCmd) Examples() []string {
	return []string{
		"# install the hooks (if needed)",
		"pre-commit # install the hooks (if needed), then run the pre-commit hook",
	}
}

func (c *hookCmd) Describe() string {
	return "Install git hooks for Perkeep, and if given, run the hook given as argument. Currently available hooks are: " + strings.TrimSuffix(strings.Join(hookFiles, ", "), ",") + "."
}

func (c *hookCmd) RunCommand(args []string) error {
	if err := c.installHook(); err != nil {
		return err
	}
	if len(args) == 0 {
		return nil
	}
	switch args[0] {
	case "pre-commit":
		if err := c.hookPreCommit(args[1:]); err != nil {
			if !(len(args) > 1 && args[1] == "test") {
				printf("You can override these checks with 'git commit --no-verify'\n")
			}
			cmdmain.ExitWithFailure = true
			return err
		}
	case "commit-msg":
		if err := c.hookCommitMsg(args[1:]); err != nil {
			cmdmain.ExitWithFailure = true
			return err
		}
	}
	return nil
}

// stripComments strips lines that begin with "#" and removes the diff section
// contained in verbose commits.
func stripComments(in []byte) []byte {
	if i := bytes.Index(in, ignoreBelow); i >= 0 {
		in = in[:i+1]
	}
	return regexp.MustCompile(`(?m)^#.*\n`).ReplaceAll(in, nil)
}

// hookCommitMsg is installed as the git commit-msg hook.
// It adds a Change-Id line to the bottom of the commit message
// if there is not one already.
// Code mostly copied from golang.org/x/review/git-codereview/hook.go
func (c *hookCmd) hookCommitMsg(args []string) error {
	if len(args) != 1 {
		return errors.New("usage: devcam hook commit-msg message.txt")
	}

	file := args[0]
	oldData, err := ioutil.ReadFile(file)
	if err != nil {
		return err
	}
	data := append([]byte{}, oldData...)
	data = stripComments(data)

	// Empty message not allowed.
	if len(bytes.TrimSpace(data)) == 0 {
		return errors.New("empty commit message")
	}

	// Insert a blank line between first line and subsequent lines if not present.
	eol := bytes.IndexByte(data, '\n')
	if eol != -1 && len(data) > eol+1 && data[eol+1] != '\n' {
		data = append(data, 0)
		copy(data[eol+1:], data[eol:])
		data[eol+1] = '\n'
	}

	// Complain if two Change-Ids are present.
	// This can happen during an interactive rebase;
	// it is easy to forget to remove one of them.
	nChangeId := bytes.Count(data, []byte("\nChange-Id: "))
	if nChangeId > 1 {
		return errors.New("multiple Change-Id lines")
	}

	// Add Change-Id to commit message if not present.
	if nChangeId == 0 {
		n := len(data)
		for n > 0 && data[n-1] == '\n' {
			n--
		}
		var id [20]byte
		if _, err := io.ReadFull(rand.Reader, id[:]); err != nil {
			return fmt.Errorf("could not generate Change-Id: %v", err)
		}
		data = append(data[:n], fmt.Sprintf("\n\nChange-Id: I%x\n", id[:])...)
	}

	// Write back.
	if !bytes.Equal(data, oldData) {
		return ioutil.WriteFile(file, data, 0666)
	}
	return nil
}

// hookPreCommit does the following checks, in order:
// gofmt, and trailing space.
// If appropriate, any one of these checks prints the action
// required from the user, and the following checks are not
// performed.
func (c *hookCmd) hookPreCommit(args []string) (err error) {
	if err = c.hookGofmt(); err != nil {
		return err
	}
	return c.hookTrailingSpace()
}

// hookGofmt runs a gofmt check on the local files matching the files in the
// git staging area.
// An error is returned if something went wrong or if some of the files need
// gofmting. In the latter case, the instruction is printed.
func (c *hookCmd) hookGofmt() error {
	if os.Getenv("GIT_GOFMT_HOOK") == "off" {
		printf("gofmt disabled by $GIT_GOFMT_HOOK=off\n")
		return nil
	}

	files, err := c.runGofmt()
	if err != nil {
		printf("gofmt hook reported errors:\n\t%v\n", strings.Replace(strings.TrimSpace(err.Error()), "\n", "\n\t", -1))
		return errors.New("gofmt errors")
	}
	if len(files) == 0 {
		return nil
	}
	printf("You need to format with gofmt:\n\tgofmt -w %s\n",
		strings.Join(files, " "))
	return errors.New("gofmt required")
}

func (c *hookCmd) hookTrailingSpace() error {
	// see 'pathspec' for the ':!' syntax to ignore a directory.
	out, _ := cmdOutputDirErr(".", "git", "diff-index", "--check", "--diff-filter=ACM", "--cached", "HEAD", "--", ".", ":!/vendor/")
	if out != "" {
		printf("\n%s", out)
		printf("Trailing whitespace detected, you need to clean it up manually.\n")
		return errors.New("trailing whitespace")
	}
	return nil
}

// runGofmt runs the external gofmt command over the local version of staged files.
// It returns the files that need gofmting.
func (c *hookCmd) runGofmt() (files []string, err error) {
	repo, err := repoRoot()
	if err != nil {
		return nil, err
	}
	if !strings.HasSuffix(repo, string(filepath.Separator)) {
		repo += string(filepath.Separator)
	}

	out, err := cmdOutputDirErr(".", "git", "diff-index", "--name-only", "--diff-filter=ACM", "--cached", "HEAD", "--", ":(glob)**/*.go", ":!/vendor/")
	if err != nil {
		return nil, err
	}
	indexFiles := addRoot(repo, nonBlankLines(out))
	if len(indexFiles) == 0 {
		return
	}

	args := []string{"-l"}
	// TODO(mpl): it would be nice to TrimPrefix the pwd from each file to get a shorter output.
	// However, since git sets the pwd to GIT_DIR before running the pre-commit hook, we lost
	// the actual pwd from when we ran `git commit`, so no dice so far.
	for _, file := range indexFiles {
		args = append(args, file)
	}

	if c.verbose {
		fmt.Fprintln(cmdmain.Stderr, commandString("gofmt", args))
	}
	cmd := exec.Command("gofmt", args...)
	var stdout, stderr bytes.Buffer
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr
	err = cmd.Run()

	if err != nil {
		// Error but no stderr: usually can't find gofmt.
		if stderr.Len() == 0 {
			return nil, fmt.Errorf("invoking gofmt: %v", err)
		}
		return nil, fmt.Errorf("%s: %v", stderr.String(), err)
	}

	// Build file list.
	files = lines(stdout.String())
	sort.Strings(files)
	return files, nil
}

func printf(format string, args ...interface{}) {
	cmdmain.Errorf(format, args...)
}

func addRoot(root string, list []string) []string {
	var out []string
	for _, x := range list {
		out = append(out, filepath.Join(root, x))
	}
	return out
}

// nonBlankLines returns the non-blank lines in text.
func nonBlankLines(text string) []string {
	var out []string
	for _, s := range lines(text) {
		if strings.TrimSpace(s) != "" {
			out = append(out, s)
		}
	}
	return out
}

// filter returns the elements in list satisfying f.
func filter(f func(string) bool, list []string) []string {
	var out []string
	for _, x := range list {
		if f(x) {
			out = append(out, x)
		}
	}
	return out
}

func commandString(command string, args []string) string {
	return strings.Join(append([]string{command}, args...), " ")
}

func lines(text string) []string {
	out := strings.Split(text, "\n")
	// Split will include a "" after the last line. Remove it.
	if n := len(out) - 1; n >= 0 && out[n] == "" {
		out = out[:n]
	}
	return out
}

func (c *hookCmd) verbosef(format string, args ...interface{}) {
	if c.verbose {
		fmt.Fprintf(cmdmain.Stdout, format, args...)
	}
}

// cmdOutputDirErr runs the command line in dir, returning its output
// and any error results.
//
// NOTE: cmdOutputDirErr must be used only to run commands that read state,
// not for commands that make changes. Commands that make changes
// should be run using runDirErr so that the -v and -n flags apply to them.
func cmdOutputDirErr(dir, command string, args ...string) (string, error) {
	// NOTE: We only show these non-state-modifying commands with -v -v.
	// Otherwise things like 'git sync -v' show all our internal "find out about
	// the git repo" commands, which is confusing if you are just trying to find
	// out what git sync means.

	cmd := exec.Command(command, args...)
	if dir != "." {
		cmd.Dir = dir
	}
	b, err := cmd.CombinedOutput()
	return string(b), err
}
<|endoftext|>


<commit_before>package main

import "net"

// CIDRNet is a net.IPNet wrapper that implements the Marshal/UnMarshal
// interface that go-flags wants.  This allows the flag parser to produce the
// error message directly, instead of us post-processing the results.
type CIDRNet struct {
	net.IPNet
}

func (n *CIDRNet) UnmarshalFlag(value string) error {
	_, ipnet, err := net.ParseCIDR(value)
	if err != nil {
		return err
	}

	n.IPNet = *ipnet
	return nil
}

func (n CIDRNet) MarshalFlag() (string, error) {
	return n.IPNet.String(), nil
}

func CIDRNetstoIPNets(networks []CIDRNet) []net.IPNet {
	var output []net.IPNet
	for _, n := range networks {
		output = append(output, n.IPNet)
	}
	return output
}
<commit_msg>Fix Go Report Card issues<commit_after>package main

import "net"

// CIDRNet is a net.IPNet wrapper that implements the Marshal/UnMarshal
// interface that go-flags wants.  This allows the flag parser to produce the
// error message directly, instead of us post-processing the results.
type CIDRNet struct {
	net.IPNet
}

// Allows go-flags package to parse CIDR blocks directly
func (n *CIDRNet) UnmarshalFlag(value string) error {
	_, ipnet, err := net.ParseCIDR(value)
	if err != nil {
		return err
	}

	n.IPNet = *ipnet
	return nil
}

// Allows go-flags package to print CIDR blocks directly.
func (n CIDRNet) MarshalFlag() (string, error) {
	return n.IPNet.String(), nil
}
<|endoftext|>


<commit_before>package dialects

import (
	"testing"
	"time"
)

func TestRandStringBytes(t *testing.T) {
	t.Log("Generates 10 length random string.")
	if s := RandStringBytes(10); len(s) != 10 {
		t.Errorf("Expected length was 10 but it was %d instead.", len(s))
	}
}

func TestResolvePath(t *testing.T) {
	t.Log("Detecting placeholders in the file path")

	p := ResolvePath("directory/subdirectory")
	if exp := "directory/subdirectory"; exp != p {
		t.Errorf("Expected path was %s but it was %s instead", exp, p)
	}

	p = ResolvePath("directory/{date}")
	if exp := "directory/" + time.Now().UTC().Format("2006-01-02"); exp != p {
		t.Errorf("Expected path was %s but it was %s instead", exp, p)
	}
}

func TestGetRandomPath(t *testing.T) {
	t.Log("Generates random file name, for `csv` extension")

	p := GetRandomPath("", "csv")
	if ext := p[len(p)-6:]; ext != "csv.gz" {
		t.Errorf("Expected extension was csv.gz but it was %s instead", ext)
	}

	p = GetRandomPath("", "csv")
	if exp := 38; len(p) != exp {
		t.Errorf("Expected length was %d without path but it was %d instead", exp, len(p))
	}

	p = GetRandomPath("dir", "csv")
	if exp := 42; len(p) != exp {
		t.Errorf("Expected length was %d with path but it was %d instead", exp, len(p))
	}

	p = GetRandomPath("dir/to/path/", "csv")
	if dir := p[0:12]; dir != "dir/to/path/" {
		t.Errorf("Expected directory path was dir/to/path/ but it was %s instead", dir)
	}

	p = GetRandomPath("dir/to/path", "csv")
	if dir := p[0:12]; dir != "dir/to/path/" {
		t.Errorf("Expected directory path was dir/to/path/ but it was %s instead", dir)
	}
}
<commit_msg>add missing test<commit_after>package dialects

import (
	"testing"
	"time"
)

func TestRandStringBytes(t *testing.T) {
	t.Log("Generates 10 length random string.")
	if s := RandStringBytes(10); len(s) != 10 {
		t.Errorf("Expected length was 10 but it was %d instead.", len(s))
	}
}

func TestResolvePath(t *testing.T) {
	t.Log("Detecting placeholders in the file path")

	p := ResolvePath("directory/subdirectory")
	if exp := "directory/subdirectory"; exp != p {
		t.Errorf("Expected path was %s but it was %s instead", exp, p)
	}

	p = ResolvePath("directory/{date}")
	if exp := "directory/" + time.Now().UTC().Format("2006-01-02"); exp != p {
		t.Errorf("Expected path was %s but it was %s instead", exp, p)
	}
}

func TestGetRandomPath(t *testing.T) {
	t.Log("Generates random file name, for `csv` extension")

	p := GetRandomPath("", "csv")
	if ext := p[len(p)-6:]; ext != "csv.gz" {
		t.Errorf("Expected extension was csv.gz but it was %s instead", ext)
	}

	p = GetRandomPath("", "csv")
	if exp := 38; len(p) != exp {
		t.Errorf("Expected length was %d without path but it was %d instead", exp, len(p))
	}

	p = GetRandomPath("", "csv")
	if p[0] == '/' {
		t.Errorf("Expected first charater was not / without path but it was %s", p[0])
	}

	p = GetRandomPath("dir", "csv")
	if exp := 42; len(p) != exp {
		t.Errorf("Expected length was %d with path but it was %d instead", exp, len(p))
	}

	p = GetRandomPath("dir/to/path/", "csv")
	if dir := p[0:12]; dir != "dir/to/path/" {
		t.Errorf("Expected directory path was dir/to/path/ but it was %s instead", dir)
	}

	p = GetRandomPath("dir/to/path", "csv")
	if dir := p[0:12]; dir != "dir/to/path/" {
		t.Errorf("Expected directory path was dir/to/path/ but it was %s instead", dir)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"

	"github.com/NebulousLabs/Sia/api"

	"github.com/spf13/cobra"
)

var (
	stopCmd = &cobra.Command{
		Use:   "stop",
		Short: "Stop the Sia daemon",
		Long:  "Stop the Sia daemon.",
		Run:   wrap(stopcmd),
	}

	updateCmd = &cobra.Command{
		Use:   "update",
		Short: "Update Sia",
		Long:  "Check for (and/or download) available updates for Sia.",
		Run:   wrap(updatecmd),
	}

	updateCheckCmd = &cobra.Command{
		Use:   "check",
		Short: "Check for available updates",
		Long:  "Check for available updates.",
		Run:   wrap(updatecheckcmd),
	}
)

// stopcmd is the handler for the command `siac stop`.
// Stops the daemon.
func stopcmd() {
	err := get("/daemon/stop")
	if err != nil {
		die("Could not stop daemon:", err)
	}
	fmt.Println("Sia daemon stopped.")
}

func updatecmd() {
	var update api.UpdateInfo
	err := getAPI("/daemon/update", &update)
	if err != nil {
		fmt.Println("Could not check for update:", err)
		return
	}
	if !update.Available {
		fmt.Println("Already up to date.")
	}

	err = post("/daemon/update", "")
	if err != nil {
		fmt.Println("Could not apply update:", err)
		return
	}
	fmt.Printf("Updated to version %s! Restart siad now.\n", update.Version)

}

func updatecheckcmd() {
	var update api.UpdateInfo
	err := getAPI("/daemon/update", &update)
	if err != nil {
		fmt.Println("Could not check for update:", err)
		return
	}
	if update.Available {
		fmt.Printf("A new release (v%s) is available! Run 'siac update' to install it.\n", update.Version)
	} else {
		fmt.Println("Up to date.")
	}
}
<commit_msg>fix siac update response<commit_after>package main

import (
	"fmt"

	"github.com/NebulousLabs/Sia/api"

	"github.com/spf13/cobra"
)

var (
	stopCmd = &cobra.Command{
		Use:   "stop",
		Short: "Stop the Sia daemon",
		Long:  "Stop the Sia daemon.",
		Run:   wrap(stopcmd),
	}

	updateCmd = &cobra.Command{
		Use:   "update",
		Short: "Update Sia",
		Long:  "Check for (and/or download) available updates for Sia.",
		Run:   wrap(updatecmd),
	}

	updateCheckCmd = &cobra.Command{
		Use:   "check",
		Short: "Check for available updates",
		Long:  "Check for available updates.",
		Run:   wrap(updatecheckcmd),
	}
)

// stopcmd is the handler for the command `siac stop`.
// Stops the daemon.
func stopcmd() {
	err := get("/daemon/stop")
	if err != nil {
		die("Could not stop daemon:", err)
	}
	fmt.Println("Sia daemon stopped.")
}

func updatecmd() {
	var update api.UpdateInfo
	err := getAPI("/daemon/update", &update)
	if err != nil {
		fmt.Println("Could not check for update:", err)
		return
	}
	if !update.Available {
		fmt.Println("Already up to date.")
		return
	}

	err = post("/daemon/update", "")
	if err != nil {
		fmt.Println("Could not apply update:", err)
		return
	}
	fmt.Printf("Updated to version %s! Restart siad now.\n", update.Version)

}

func updatecheckcmd() {
	var update api.UpdateInfo
	err := getAPI("/daemon/update", &update)
	if err != nil {
		fmt.Println("Could not check for update:", err)
		return
	}
	if update.Available {
		fmt.Printf("A new release (v%s) is available! Run 'siac update' to install it.\n", update.Version)
	} else {
		fmt.Println("Up to date.")
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 The etcd 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 command

import (
	"fmt"
	"os"
	"sync"
	"time"

	v3 "github.com/coreos/etcd/clientv3"
	"github.com/coreos/etcd/etcdserver/api/v3rpc/rpctypes"
	"github.com/coreos/etcd/pkg/flags"

	"github.com/spf13/cobra"
)

var epClusterEndpoints bool
var epHashKVRev int64

// NewEndpointCommand returns the cobra command for "endpoint".
func NewEndpointCommand() *cobra.Command {
	ec := &cobra.Command{
		Use:   "endpoint <subcommand>",
		Short: "Endpoint related commands",
	}

	ec.PersistentFlags().BoolVar(&epClusterEndpoints, "cluster", false, "use all endpoints from the cluster member list")
	ec.AddCommand(newEpHealthCommand())
	ec.AddCommand(newEpStatusCommand())
	ec.AddCommand(newEpHashKVCommand())

	return ec
}

func newEpHealthCommand() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "health",
		Short: "Checks the healthiness of endpoints specified in `--endpoints` flag",
		Run:   epHealthCommandFunc,
	}

	return cmd
}

func newEpStatusCommand() *cobra.Command {
	return &cobra.Command{
		Use:   "status",
		Short: "Prints out the status of endpoints specified in `--endpoints` flag",
		Long: `When --write-out is set to simple, this command prints out comma-separated status lists for each endpoint.
The items in the lists are endpoint, ID, version, db size, is leader, raft term, raft index.
`,
		Run: epStatusCommandFunc,
	}
}

func newEpHashKVCommand() *cobra.Command {
	hc := &cobra.Command{
		Use:   "hashkv",
		Short: "Prints the KV history hash for each endpoint in --endpoints",
		Run:   epHashKVCommandFunc,
	}
	hc.PersistentFlags().Int64Var(&epHashKVRev, "rev", 0, "maximum revision to hash (default: all revisions)")
	return hc
}

// epHealthCommandFunc executes the "endpoint-health" command.
func epHealthCommandFunc(cmd *cobra.Command, args []string) {
	flags.SetPflagsFromEnv("ETCDCTL", cmd.InheritedFlags())

	sec := secureCfgFromCmd(cmd)
	dt := dialTimeoutFromCmd(cmd)
	ka := keepAliveTimeFromCmd(cmd)
	kat := keepAliveTimeoutFromCmd(cmd)
	auth := authCfgFromCmd(cmd)
	cfgs := []*v3.Config{}
	for _, ep := range endpointsFromCluster(cmd) {
		cfg, err := newClientCfg([]string{ep}, dt, ka, kat, sec, auth)
		if err != nil {
			ExitWithError(ExitBadArgs, err)
		}
		cfgs = append(cfgs, cfg)
	}

	var wg sync.WaitGroup
	errc := make(chan error, len(cfgs))
	for _, cfg := range cfgs {
		wg.Add(1)
		go func(cfg *v3.Config) {
			defer wg.Done()
			ep := cfg.Endpoints[0]
			cli, err := v3.New(*cfg)
			if err != nil {
				errc <- fmt.Errorf("%s is unhealthy: failed to connect: %v", ep, err)
				return
			}
			st := time.Now()
			// get a random key. As long as we can get the response without an error, the
			// endpoint is health.
			ctx, cancel := commandCtx(cmd)
			_, err = cli.Get(ctx, "health")
			cancel()
			// permission denied is OK since proposal goes through consensus to get it
			if err == nil || err == rpctypes.ErrPermissionDenied {
				fmt.Printf("%s is healthy: successfully committed proposal: took = %v\n", ep, time.Since(st))
			} else {
				errc <- fmt.Errorf("%s is unhealthy: failed to commit proposal: %v", ep, err)
			}
		}(cfg)
	}

	wg.Wait()
	close(errc)

	errs := false
	for err := range errc {
		if err != nil {
			errs = true
			fmt.Fprintln(os.Stderr, err)
		}
	}
	if errs {
		ExitWithError(ExitError, fmt.Errorf("unhealthy cluster"))
	}
}

type epStatus struct {
	Ep   string             `json:"Endpoint"`
	Resp *v3.StatusResponse `json:"Status"`
}

func epStatusCommandFunc(cmd *cobra.Command, args []string) {
	c := mustClientFromCmd(cmd)

	statusList := []epStatus{}
	var err error
	for _, ep := range endpointsFromCluster(cmd) {
		ctx, cancel := commandCtx(cmd)
		resp, serr := c.Status(ctx, ep)
		cancel()
		if serr != nil {
			err = serr
			fmt.Fprintf(os.Stderr, "Failed to get the status of endpoint %s (%v)\n", ep, serr)
			continue
		}
		statusList = append(statusList, epStatus{Ep: ep, Resp: resp})
	}

	display.EndpointStatus(statusList)

	if err != nil {
		os.Exit(ExitError)
	}
}

type epHashKV struct {
	Ep   string             `json:"Endpoint"`
	Resp *v3.HashKVResponse `json:"HashKV"`
}

func epHashKVCommandFunc(cmd *cobra.Command, args []string) {
	c := mustClientFromCmd(cmd)

	hashList := []epHashKV{}
	var err error
	for _, ep := range endpointsFromCluster(cmd) {
		ctx, cancel := commandCtx(cmd)
		resp, serr := c.HashKV(ctx, ep, epHashKVRev)
		cancel()
		if serr != nil {
			err = serr
			fmt.Fprintf(os.Stderr, "Failed to get the hash of endpoint %s (%v)\n", ep, serr)
			continue
		}
		hashList = append(hashList, epHashKV{Ep: ep, Resp: resp})
	}

	display.EndpointHashKV(hashList)

	if err != nil {
		ExitWithError(ExitError, err)
	}
}

func endpointsFromCluster(cmd *cobra.Command) []string {
	if !epClusterEndpoints {
		endpoints, err := cmd.Flags().GetStringSlice("endpoints")
		if err != nil {
			ExitWithError(ExitError, err)
		}
		return endpoints
	}
	c := mustClientFromCmd(cmd)
	ctx, cancel := commandCtx(cmd)
	defer func() {
		c.Close()
		cancel()
	}()
	membs, err := c.MemberList(ctx)
	if err != nil {
		err = fmt.Errorf("failed to fetch endpoints from etcd cluster member list: %v", err)
		ExitWithError(ExitError, err)
	}

	ret := []string{}
	for _, m := range membs.Members {
		ret = append(ret, m.ClientURLs...)
	}
	return ret
}
<commit_msg>etcdctl: don't ask password twice for etcdctl endpoint health --cluster<commit_after>// Copyright 2015 The etcd 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 command

import (
	"fmt"
	"os"
	"sync"
	"time"

	v3 "github.com/coreos/etcd/clientv3"
	"github.com/coreos/etcd/etcdserver/api/v3rpc/rpctypes"
	"github.com/coreos/etcd/pkg/flags"

	"github.com/spf13/cobra"
)

var epClusterEndpoints bool
var epHashKVRev int64

// NewEndpointCommand returns the cobra command for "endpoint".
func NewEndpointCommand() *cobra.Command {
	ec := &cobra.Command{
		Use:   "endpoint <subcommand>",
		Short: "Endpoint related commands",
	}

	ec.PersistentFlags().BoolVar(&epClusterEndpoints, "cluster", false, "use all endpoints from the cluster member list")
	ec.AddCommand(newEpHealthCommand())
	ec.AddCommand(newEpStatusCommand())
	ec.AddCommand(newEpHashKVCommand())

	return ec
}

func newEpHealthCommand() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "health",
		Short: "Checks the healthiness of endpoints specified in `--endpoints` flag",
		Run:   epHealthCommandFunc,
	}

	return cmd
}

func newEpStatusCommand() *cobra.Command {
	return &cobra.Command{
		Use:   "status",
		Short: "Prints out the status of endpoints specified in `--endpoints` flag",
		Long: `When --write-out is set to simple, this command prints out comma-separated status lists for each endpoint.
The items in the lists are endpoint, ID, version, db size, is leader, raft term, raft index.
`,
		Run: epStatusCommandFunc,
	}
}

func newEpHashKVCommand() *cobra.Command {
	hc := &cobra.Command{
		Use:   "hashkv",
		Short: "Prints the KV history hash for each endpoint in --endpoints",
		Run:   epHashKVCommandFunc,
	}
	hc.PersistentFlags().Int64Var(&epHashKVRev, "rev", 0, "maximum revision to hash (default: all revisions)")
	return hc
}

// epHealthCommandFunc executes the "endpoint-health" command.
func epHealthCommandFunc(cmd *cobra.Command, args []string) {
	flags.SetPflagsFromEnv("ETCDCTL", cmd.InheritedFlags())

	sec := secureCfgFromCmd(cmd)
	dt := dialTimeoutFromCmd(cmd)
	ka := keepAliveTimeFromCmd(cmd)
	kat := keepAliveTimeoutFromCmd(cmd)
	auth := authCfgFromCmd(cmd)
	cfgs := []*v3.Config{}
	for _, ep := range endpointsFromCluster(cmd) {
		cfg, err := newClientCfg([]string{ep}, dt, ka, kat, sec, auth)
		if err != nil {
			ExitWithError(ExitBadArgs, err)
		}
		cfgs = append(cfgs, cfg)
	}

	var wg sync.WaitGroup
	errc := make(chan error, len(cfgs))
	for _, cfg := range cfgs {
		wg.Add(1)
		go func(cfg *v3.Config) {
			defer wg.Done()
			ep := cfg.Endpoints[0]
			cli, err := v3.New(*cfg)
			if err != nil {
				errc <- fmt.Errorf("%s is unhealthy: failed to connect: %v", ep, err)
				return
			}
			st := time.Now()
			// get a random key. As long as we can get the response without an error, the
			// endpoint is health.
			ctx, cancel := commandCtx(cmd)
			_, err = cli.Get(ctx, "health")
			cancel()
			// permission denied is OK since proposal goes through consensus to get it
			if err == nil || err == rpctypes.ErrPermissionDenied {
				fmt.Printf("%s is healthy: successfully committed proposal: took = %v\n", ep, time.Since(st))
			} else {
				errc <- fmt.Errorf("%s is unhealthy: failed to commit proposal: %v", ep, err)
			}
		}(cfg)
	}

	wg.Wait()
	close(errc)

	errs := false
	for err := range errc {
		if err != nil {
			errs = true
			fmt.Fprintln(os.Stderr, err)
		}
	}
	if errs {
		ExitWithError(ExitError, fmt.Errorf("unhealthy cluster"))
	}
}

type epStatus struct {
	Ep   string             `json:"Endpoint"`
	Resp *v3.StatusResponse `json:"Status"`
}

func epStatusCommandFunc(cmd *cobra.Command, args []string) {
	c := mustClientFromCmd(cmd)

	statusList := []epStatus{}
	var err error
	for _, ep := range endpointsFromCluster(cmd) {
		ctx, cancel := commandCtx(cmd)
		resp, serr := c.Status(ctx, ep)
		cancel()
		if serr != nil {
			err = serr
			fmt.Fprintf(os.Stderr, "Failed to get the status of endpoint %s (%v)\n", ep, serr)
			continue
		}
		statusList = append(statusList, epStatus{Ep: ep, Resp: resp})
	}

	display.EndpointStatus(statusList)

	if err != nil {
		os.Exit(ExitError)
	}
}

type epHashKV struct {
	Ep   string             `json:"Endpoint"`
	Resp *v3.HashKVResponse `json:"HashKV"`
}

func epHashKVCommandFunc(cmd *cobra.Command, args []string) {
	c := mustClientFromCmd(cmd)

	hashList := []epHashKV{}
	var err error
	for _, ep := range endpointsFromCluster(cmd) {
		ctx, cancel := commandCtx(cmd)
		resp, serr := c.HashKV(ctx, ep, epHashKVRev)
		cancel()
		if serr != nil {
			err = serr
			fmt.Fprintf(os.Stderr, "Failed to get the hash of endpoint %s (%v)\n", ep, serr)
			continue
		}
		hashList = append(hashList, epHashKV{Ep: ep, Resp: resp})
	}

	display.EndpointHashKV(hashList)

	if err != nil {
		ExitWithError(ExitError, err)
	}
}

func endpointsFromCluster(cmd *cobra.Command) []string {
	if !epClusterEndpoints {
		endpoints, err := cmd.Flags().GetStringSlice("endpoints")
		if err != nil {
			ExitWithError(ExitError, err)
		}
		return endpoints
	}

	sec := secureCfgFromCmd(cmd)
	dt := dialTimeoutFromCmd(cmd)
	ka := keepAliveTimeFromCmd(cmd)
	kat := keepAliveTimeoutFromCmd(cmd)
	eps, err := endpointsFromCmd(cmd)
	if err != nil {
		ExitWithError(ExitError, err)
	}
	// exclude auth for not asking needless password (MemberList() doesn't need authentication)

	cfg, err := newClientCfg(eps, dt, ka, kat, sec, nil)
	if err != nil {
		ExitWithError(ExitError, err)
	}
	c, err := v3.New(*cfg)
	if err != nil {
		ExitWithError(ExitError, err)
	}

	ctx, cancel := commandCtx(cmd)
	defer func() {
		c.Close()
		cancel()
	}()
	membs, err := c.MemberList(ctx)
	if err != nil {
		err = fmt.Errorf("failed to fetch endpoints from etcd cluster member list: %v", err)
		ExitWithError(ExitError, err)
	}

	ret := []string{}
	for _, m := range membs.Members {
		ret = append(ret, m.ClientURLs...)
	}
	return ret
}
<|endoftext|>


<commit_before>// Copyright 2017, OpenCensus 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_test

import (
	"fmt"
	"io"
	"net"
	"testing"
	"time"

	"go.opencensus.io/plugin/grpc/grpctrace"
	testpb "go.opencensus.io/plugin/grpc/grpctrace/testdata"
	"go.opencensus.io/trace"
	"golang.org/x/net/context"
	"google.golang.org/grpc"
)

type testServer struct{}

func (s *testServer) Single(ctx context.Context, in *testpb.FooRequest) (*testpb.FooResponse, error) {
	if in.Fail {
		return nil, fmt.Errorf("request failed")
	}
	return &testpb.FooResponse{}, nil
}

func (s *testServer) Multiple(stream testpb.Foo_MultipleServer) error {
	for {
		in, err := stream.Recv()
		if err == io.EOF {
			return nil
		}
		if err != nil {
			return err
		}
		if in.Fail {
			return fmt.Errorf("request failed")
		}
		if err := stream.Send(&testpb.FooResponse{}); err != nil {
			return err
		}
	}
}

func newTestClientAndServer() (client testpb.FooClient, server *grpc.Server, cleanup func(), err error) {
	// initialize server
	listener, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		return nil, nil, nil, fmt.Errorf("net.Listen: %v", err)
	}
	server = grpc.NewServer(grpc.StatsHandler(&grpctrace.ServerStatsHandler{}))
	testpb.RegisterFooServer(server, &testServer{})
	go server.Serve(listener)

	// initialize client
	clientConn, err := grpc.Dial(listener.Addr().String(), grpc.WithInsecure(), grpc.WithStatsHandler(&grpctrace.ClientStatsHandler{}), grpc.WithBlock())
	if err != nil {
		return nil, nil, nil, fmt.Errorf("grpc.Dial: %v", err)
	}
	client = testpb.NewFooClient(clientConn)

	cleanup = func() {
		server.GracefulStop()
		clientConn.Close()
	}

	return client, server, cleanup, nil
}

type testExporter struct {
	ch chan *trace.SpanData
}

func (t *testExporter) ExportSpan(s *trace.SpanData) {
	go func() { t.ch <- s }()
}

func TestStreaming(t *testing.T) {
	trace.SetDefaultSampler(trace.AlwaysSample())
	te := testExporter{make(chan *trace.SpanData)}
	trace.RegisterExporter(&te)
	defer trace.UnregisterExporter(&te)

	client, _, cleanup, err := newTestClientAndServer()
	if err != nil {
		t.Fatalf("initializing client and server: %v", err)
	}

	stream, err := client.Multiple(context.Background())
	if err != nil {
		t.Fatalf("Call failed: %v", err)
	}

	err = stream.Send(&testpb.FooRequest{})
	if err != nil {
		t.Fatalf("Couldn't send streaming request: %v", err)
	}
	stream.CloseSend()

	for {
		_, err := stream.Recv()
		if err == io.EOF {
			break
		}
		if err != nil {
			t.Errorf("stream.Recv() = %v; want no errors", err)
		}
	}

	cleanup()

	s1 := <-te.ch
	s2 := <-te.ch

	checkSpanData(t, s1, s2, ".testdata.Foo.Multiple", true)

	select {
	case <-te.ch:
		t.Fatal("received extra exported spans")
	case <-time.After(time.Second / 10):
	}
}

func TestStreamingFail(t *testing.T) {
	t.Skipf("Skipping due to the behavioral change at https://github.com/grpc/grpc-go/pull/1854")

	// TODO(jbd): Enable test again as soon as span.End is invoked
	// for the outgoing RPCs properly.

	trace.SetDefaultSampler(trace.AlwaysSample())
	te := testExporter{make(chan *trace.SpanData)}
	trace.RegisterExporter(&te)
	defer trace.UnregisterExporter(&te)

	client, _, cleanup, err := newTestClientAndServer()
	if err != nil {
		t.Fatalf("initializing client and server: %v", err)
	}

	stream, err := client.Multiple(context.Background())
	if err != nil {
		t.Fatalf("Call failed: %v", err)
	}

	err = stream.Send(&testpb.FooRequest{Fail: true})
	if err != nil {
		t.Fatalf("Couldn't send streaming request: %v", err)
	}
	stream.CloseSend()

	for {
		_, err := stream.Recv()
		if err == io.EOF {
			break
		}
		if err == nil {
			t.Error("stream.Recv() = nil; want errors")
		}
	}

	s1 := <-te.ch
	s2 := <-te.ch

	checkSpanData(t, s1, s2, ".testdata.Foo.Multiple", false)
	cleanup()

	select {
	case <-te.ch:
		t.Fatal("received extra exported spans")
	case <-time.After(time.Second / 10):
	}
}

func TestSingle(t *testing.T) {
	trace.SetDefaultSampler(trace.AlwaysSample())
	te := testExporter{make(chan *trace.SpanData)}
	trace.RegisterExporter(&te)
	defer trace.UnregisterExporter(&te)

	client, _, cleanup, err := newTestClientAndServer()
	if err != nil {
		t.Fatalf("initializing client and server: %v", err)
	}

	_, err = client.Single(context.Background(), &testpb.FooRequest{})
	if err != nil {
		t.Fatalf("Couldn't send request: %v", err)
	}

	s1 := <-te.ch
	s2 := <-te.ch

	checkSpanData(t, s1, s2, ".testdata.Foo.Single", true)
	cleanup()

	select {
	case <-te.ch:
		t.Fatal("received extra exported spans")
	case <-time.After(time.Second / 10):
	}
}

func TestSingleFail(t *testing.T) {
	trace.SetDefaultSampler(trace.AlwaysSample())
	te := testExporter{make(chan *trace.SpanData)}
	trace.RegisterExporter(&te)
	defer trace.UnregisterExporter(&te)

	client, _, cleanup, err := newTestClientAndServer()
	if err != nil {
		t.Fatalf("initializing client and server: %v", err)
	}

	_, err = client.Single(context.Background(), &testpb.FooRequest{Fail: true})
	if err == nil {
		t.Fatalf("Got nil error from request, want non-nil")
	}

	s1 := <-te.ch
	s2 := <-te.ch

	checkSpanData(t, s1, s2, ".testdata.Foo.Single", false)
	cleanup()

	select {
	case <-te.ch:
		t.Fatal("received extra exported spans")
	case <-time.After(time.Second / 10):
	}
}

func checkSpanData(t *testing.T, s1, s2 *trace.SpanData, methodName string, success bool) {
	t.Helper()

	if s1.Name < s2.Name {
		s1, s2 = s2, s1
	}

	if got, want := s1.Name, "Sent"+methodName; got != want {
		t.Errorf("Got name %q want %q", got, want)
	}
	if got, want := s2.Name, "Recv"+methodName; got != want {
		t.Errorf("Got name %q want %q", got, want)
	}
	if got, want := s2.SpanContext.TraceID, s1.SpanContext.TraceID; got != want {
		t.Errorf("Got trace IDs %s and %s, want them equal", got, want)
	}
	if got, want := s2.ParentSpanID, s1.SpanContext.SpanID; got != want {
		t.Errorf("Got ParentSpanID %s, want %s", got, want)
	}
	if got := (s1.Status.Code == 0); got != success {
		t.Errorf("Got success=%t want %t", got, success)
	}
	if got := (s2.Status.Code == 0); got != success {
		t.Errorf("Got success=%t want %t", got, success)
	}
	if s1.HasRemoteParent {
		t.Errorf("Got HasRemoteParent=%t, want false", s1.HasRemoteParent)
	}
	if !s2.HasRemoteParent {
		t.Errorf("Got HasRemoteParent=%t, want true", s2.HasRemoteParent)
	}
}
<commit_msg>Enable streaming gRPC test (#433)<commit_after>// Copyright 2017, OpenCensus 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_test

import (
	"fmt"
	"io"
	"net"
	"testing"
	"time"

	"go.opencensus.io/plugin/grpc/grpctrace"
	testpb "go.opencensus.io/plugin/grpc/grpctrace/testdata"
	"go.opencensus.io/trace"
	"golang.org/x/net/context"
	"google.golang.org/grpc"
)

type testServer struct{}

func (s *testServer) Single(ctx context.Context, in *testpb.FooRequest) (*testpb.FooResponse, error) {
	if in.Fail {
		return nil, fmt.Errorf("request failed")
	}
	return &testpb.FooResponse{}, nil
}

func (s *testServer) Multiple(stream testpb.Foo_MultipleServer) error {
	for {
		in, err := stream.Recv()
		if err == io.EOF {
			return nil
		}
		if err != nil {
			return err
		}
		if in.Fail {
			return fmt.Errorf("request failed")
		}
		if err := stream.Send(&testpb.FooResponse{}); err != nil {
			return err
		}
	}
}

func newTestClientAndServer() (client testpb.FooClient, server *grpc.Server, cleanup func(), err error) {
	// initialize server
	listener, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		return nil, nil, nil, fmt.Errorf("net.Listen: %v", err)
	}
	server = grpc.NewServer(grpc.StatsHandler(&grpctrace.ServerStatsHandler{}))
	testpb.RegisterFooServer(server, &testServer{})
	go server.Serve(listener)

	// initialize client
	clientConn, err := grpc.Dial(listener.Addr().String(), grpc.WithInsecure(), grpc.WithStatsHandler(&grpctrace.ClientStatsHandler{}), grpc.WithBlock())
	if err != nil {
		return nil, nil, nil, fmt.Errorf("grpc.Dial: %v", err)
	}
	client = testpb.NewFooClient(clientConn)

	cleanup = func() {
		server.GracefulStop()
		clientConn.Close()
	}

	return client, server, cleanup, nil
}

type testExporter struct {
	ch chan *trace.SpanData
}

func (t *testExporter) ExportSpan(s *trace.SpanData) {
	go func() { t.ch <- s }()
}

func TestStreaming(t *testing.T) {
	trace.SetDefaultSampler(trace.AlwaysSample())
	te := testExporter{make(chan *trace.SpanData)}
	trace.RegisterExporter(&te)
	defer trace.UnregisterExporter(&te)

	client, _, cleanup, err := newTestClientAndServer()
	if err != nil {
		t.Fatalf("initializing client and server: %v", err)
	}

	stream, err := client.Multiple(context.Background())
	if err != nil {
		t.Fatalf("Call failed: %v", err)
	}

	err = stream.Send(&testpb.FooRequest{})
	if err != nil {
		t.Fatalf("Couldn't send streaming request: %v", err)
	}
	stream.CloseSend()

	for {
		_, err := stream.Recv()
		if err == io.EOF {
			break
		}
		if err != nil {
			t.Errorf("stream.Recv() = %v; want no errors", err)
		}
	}

	cleanup()

	s1 := <-te.ch
	s2 := <-te.ch

	checkSpanData(t, s1, s2, ".testdata.Foo.Multiple", true)

	select {
	case <-te.ch:
		t.Fatal("received extra exported spans")
	case <-time.After(time.Second / 10):
	}
}

func TestStreamingFail(t *testing.T) {
	trace.SetDefaultSampler(trace.AlwaysSample())
	te := testExporter{make(chan *trace.SpanData)}
	trace.RegisterExporter(&te)
	defer trace.UnregisterExporter(&te)

	client, _, cleanup, err := newTestClientAndServer()
	if err != nil {
		t.Fatalf("initializing client and server: %v", err)
	}

	stream, err := client.Multiple(context.Background())
	if err != nil {
		t.Fatalf("Call failed: %v", err)
	}

	err = stream.Send(&testpb.FooRequest{Fail: true})
	if err != nil {
		t.Fatalf("Couldn't send streaming request: %v", err)
	}
	stream.CloseSend()

	for {
		_, err := stream.Recv()
		if err == nil || err == io.EOF {
			t.Errorf("stream.Recv() = %v; want errors", err)
		} else {
			break
		}
	}

	s1 := <-te.ch
	s2 := <-te.ch

	checkSpanData(t, s1, s2, ".testdata.Foo.Multiple", false)
	cleanup()

	select {
	case <-te.ch:
		t.Fatal("received extra exported spans")
	case <-time.After(time.Second / 10):
	}
}

func TestSingle(t *testing.T) {
	trace.SetDefaultSampler(trace.AlwaysSample())
	te := testExporter{make(chan *trace.SpanData)}
	trace.RegisterExporter(&te)
	defer trace.UnregisterExporter(&te)

	client, _, cleanup, err := newTestClientAndServer()
	if err != nil {
		t.Fatalf("initializing client and server: %v", err)
	}

	_, err = client.Single(context.Background(), &testpb.FooRequest{})
	if err != nil {
		t.Fatalf("Couldn't send request: %v", err)
	}

	s1 := <-te.ch
	s2 := <-te.ch

	checkSpanData(t, s1, s2, ".testdata.Foo.Single", true)
	cleanup()

	select {
	case <-te.ch:
		t.Fatal("received extra exported spans")
	case <-time.After(time.Second / 10):
	}
}

func TestSingleFail(t *testing.T) {
	trace.SetDefaultSampler(trace.AlwaysSample())
	te := testExporter{make(chan *trace.SpanData)}
	trace.RegisterExporter(&te)
	defer trace.UnregisterExporter(&te)

	client, _, cleanup, err := newTestClientAndServer()
	if err != nil {
		t.Fatalf("initializing client and server: %v", err)
	}

	_, err = client.Single(context.Background(), &testpb.FooRequest{Fail: true})
	if err == nil {
		t.Fatalf("Got nil error from request, want non-nil")
	}

	s1 := <-te.ch
	s2 := <-te.ch

	checkSpanData(t, s1, s2, ".testdata.Foo.Single", false)
	cleanup()

	select {
	case <-te.ch:
		t.Fatal("received extra exported spans")
	case <-time.After(time.Second / 10):
	}
}

func checkSpanData(t *testing.T, s1, s2 *trace.SpanData, methodName string, success bool) {
	t.Helper()

	if s1.Name < s2.Name {
		s1, s2 = s2, s1
	}

	if got, want := s1.Name, "Sent"+methodName; got != want {
		t.Errorf("Got name %q want %q", got, want)
	}
	if got, want := s2.Name, "Recv"+methodName; got != want {
		t.Errorf("Got name %q want %q", got, want)
	}
	if got, want := s2.SpanContext.TraceID, s1.SpanContext.TraceID; got != want {
		t.Errorf("Got trace IDs %s and %s, want them equal", got, want)
	}
	if got, want := s2.ParentSpanID, s1.SpanContext.SpanID; got != want {
		t.Errorf("Got ParentSpanID %s, want %s", got, want)
	}
	if got := (s1.Status.Code == 0); got != success {
		t.Errorf("Got success=%t want %t", got, success)
	}
	if got := (s2.Status.Code == 0); got != success {
		t.Errorf("Got success=%t want %t", got, success)
	}
	if s1.HasRemoteParent {
		t.Errorf("Got HasRemoteParent=%t, want false", s1.HasRemoteParent)
	}
	if !s2.HasRemoteParent {
		t.Errorf("Got HasRemoteParent=%t, want true", s2.HasRemoteParent)
	}
}
<|endoftext|>