<commit_before>// github.com/golang/glog fork with Airbrake integration.
//
// Copyright 2013 Google Inc. All Rights Reserved.
//
// 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 glog

import (
	"fmt"
	"strings"

	golog "github.com/golang/glog"
)

// severity identifies the sort of log: info, warning etc. It also implements
// the flag.Value interface. The -stderrthreshold flag is of type severity and
// should be modified only through the flag.Value interface. The values match
// the corresponding constants in C++.
type severity int32 // sync/atomic int32

// These constants identify the log levels in order of increasing severity.
// A message written to a high-severity log file is also written to each
// lower-severity log file.
const (
	infoLog severity = iota
	warningLog
	errorLog
	fatalLog
)

var severityName = []string{
	infoLog:    "INFO",
	warningLog: "WARNING",
	errorLog:   "ERROR",
	fatalLog:   "FATAL",
}

// Stats tracks the number of lines of output and number of bytes
// per severity level. Values must be read with atomic.LoadInt64.
var Stats = golog.Stats

// SetMaxSize sets maximum size of a log file in bytes.
func SetMaxSize(maxSize uint64) {
	golog.MaxSize = maxSize
}

func severityByName(s string) (severity, bool) {
	s = strings.ToUpper(s)
	for i, name := range severityName {
		if name == s {
			return severity(i), true
		}
	}
	return 0, false
}

// CopyStandardLogTo arranges for messages written to the Go "log" package's
// default logs to also appear in the Google logs for the named and lower
// severities.  Subsequent changes to the standard log's default output location
// or format may break this behavior.
//
// Valid names are "INFO", "WARNING", "ERROR", and "FATAL".  If the name is not
// recognized, CopyStandardLogTo panics.
func CopyStandardLogTo(name string) {
	golog.CopyStandardLogTo(name)
}

// Flush flushes all pending log I/O.
func Flush() {
	golog.Flush()
}

// Info logs to the INFO log.
// Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
func Info(args ...interface{}) {
	golog.InfoDepth(1, args...)
	notifyAirbrake(infoLog, "", args...)
}

// Infoln logs to the INFO log.
// Arguments are handled in the manner of fmt.Println; a newline is appended if missing.
func Infoln(args ...interface{}) {
	golog.InfoDepth(1, fmt.Sprintln(args...))
	notifyAirbrake(infoLog, "", args...)
}

// Infof logs to the INFO log.
// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing.
func Infof(format string, args ...interface{}) {
	golog.InfoDepth(1, fmt.Sprintf(format, args...))
	notifyAirbrake(infoLog, format, args...)
}

// Warning logs to the WARNING and INFO logs.
// Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
func Warning(args ...interface{}) {
	golog.WarningDepth(1, args...)
	notifyAirbrake(warningLog, "", args...)
}

// Warningln logs to the WARNING and INFO logs.
// Arguments are handled in the manner of fmt.Println; a newline is appended if missing.
func Warningln(args ...interface{}) {
	golog.WarningDepth(1, fmt.Sprintln(args...))
	notifyAirbrake(warningLog, "", args...)
}

// Warningf logs to the WARNING and INFO logs.
// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing.
func Warningf(format string, args ...interface{}) {
	golog.WarningDepth(1, fmt.Sprintf(format, args...))
	notifyAirbrake(warningLog, format, args...)
}

// Error logs to the ERROR, WARNING, and INFO logs.
// Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
func Error(args ...interface{}) {
	golog.ErrorDepth(1, args...)
	notifyAirbrake(errorLog, "", args...)
}

// Errorln logs to the ERROR, WARNING, and INFO logs.
// Arguments are handled in the manner of fmt.Println; a newline is appended if missing.
func Errorln(args ...interface{}) {
	golog.ErrorDepth(1, fmt.Sprintln(args...))
	notifyAirbrake(errorLog, "", args...)
}

// Errorf logs to the ERROR, WARNING, and INFO logs.
// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing.
func Errorf(format string, args ...interface{}) {
	golog.ErrorDepth(1, fmt.Sprintf(format, args...))
	notifyAirbrake(errorLog, format, args...)
}

// Fatal logs to the FATAL, ERROR, WARNING, and INFO logs,
// including a stack trace of all running goroutines, then calls os.Exit(255).
// Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
func Fatal(args ...interface{}) {
	golog.FatalDepth(1, args...)
	notifyAirbrake(fatalLog, "", args...)
}

// Fatalln logs to the FATAL, ERROR, WARNING, and INFO logs,
// including a stack trace of all running goroutines, then calls os.Exit(255).
// Arguments are handled in the manner of fmt.Println; a newline is appended if missing.
func Fatalln(args ...interface{}) {
	golog.FatalDepth(1, fmt.Sprintln(args...))
	notifyAirbrake(fatalLog, "", args...)
}

// Fatalf logs to the FATAL, ERROR, WARNING, and INFO logs,
// including a stack trace of all running goroutines, then calls os.Exit(255).
// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing.
func Fatalf(format string, args ...interface{}) {
	golog.FatalDepth(1, fmt.Sprintf(format, args...))
	notifyAirbrake(fatalLog, format, args...)
}

// Exit logs to the FATAL, ERROR, WARNING, and INFO logs, then calls os.Exit(1).
// Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
func Exit(args ...interface{}) {
	golog.ExitDepth(1, args...)
	notifyAirbrake(errorLog, "", args...)
}

// Exitln logs to the FATAL, ERROR, WARNING, and INFO logs, then calls os.Exit(1).
func Exitln(args ...interface{}) {
	golog.ExitDepth(1, fmt.Sprintln(args...))
	notifyAirbrake(errorLog, "", args...)
}

// Exitf logs to the FATAL, ERROR, WARNING, and INFO logs, then calls os.Exit(1).
// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing.
func Exitf(format string, args ...interface{}) {
	golog.ExitDepth(1, fmt.Sprintf(format, args...))
	notifyAirbrake(errorLog, format, args...)
}

// Verbose is a boolean type that implements Infof (like Printf) etc.
// See the documentation of V for more information.
type Verbose bool

// V reports whether verbosity at the call site is at least the requested level.
// The returned value is a boolean of type Verbose, which implements Info, Infoln
// and Infof. These methods will write to the Info log if called.
// Thus, one may write either
//	if glog.V(2) { glog.Info("log this") }
// or
//	glog.V(2).Info("log this")
// The second form is shorter but the first is cheaper if logging is off because it does
// not evaluate its arguments.
//
// Whether an individual call to V generates a log record depends on the setting of
// the -v and --vmodule flags; both are off by default. If the level in the call to
// V is at least the value of -v, or of -vmodule for the source file containing the
// call, the V call will log.
func V(level golog.Level) Verbose {
	return Verbose(golog.V(level))
}

// Info is equivalent to the global Info function, guarded by the value of v.
// See the documentation of V for usage.
func (v Verbose) Info(args ...interface{}) {
	if v {
		Info(args...)
	}
}

// Infoln is equivalent to the global Infoln function, guarded by the value of v.
// See the documentation of V for usage.
func (v Verbose) Infoln(args ...interface{}) {
	if v {
		Infoln(args...)
	}
}

// Infof is equivalent to the global Infof function, guarded by the value of v.
// See the documentation of V for usage.
func (v Verbose) Infof(format string, args ...interface{}) {
	if v {
		Infof(format, args...)
	}
}
<commit_msg>Flush gobrake notifier if available.<commit_after>// github.com/golang/glog fork with Airbrake integration.
//
// Copyright 2013 Google Inc. All Rights Reserved.
//
// 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 glog

import (
	"fmt"
	"strings"

	golog "github.com/golang/glog"
)

// severity identifies the sort of log: info, warning etc. It also implements
// the flag.Value interface. The -stderrthreshold flag is of type severity and
// should be modified only through the flag.Value interface. The values match
// the corresponding constants in C++.
type severity int32 // sync/atomic int32

// These constants identify the log levels in order of increasing severity.
// A message written to a high-severity log file is also written to each
// lower-severity log file.
const (
	infoLog severity = iota
	warningLog
	errorLog
	fatalLog
)

var severityName = []string{
	infoLog:    "INFO",
	warningLog: "WARNING",
	errorLog:   "ERROR",
	fatalLog:   "FATAL",
}

// Stats tracks the number of lines of output and number of bytes
// per severity level. Values must be read with atomic.LoadInt64.
var Stats = golog.Stats

// SetMaxSize sets maximum size of a log file in bytes.
func SetMaxSize(maxSize uint64) {
	golog.MaxSize = maxSize
}

func severityByName(s string) (severity, bool) {
	s = strings.ToUpper(s)
	for i, name := range severityName {
		if name == s {
			return severity(i), true
		}
	}
	return 0, false
}

// CopyStandardLogTo arranges for messages written to the Go "log" package's
// default logs to also appear in the Google logs for the named and lower
// severities.  Subsequent changes to the standard log's default output location
// or format may break this behavior.
//
// Valid names are "INFO", "WARNING", "ERROR", and "FATAL".  If the name is not
// recognized, CopyStandardLogTo panics.
func CopyStandardLogTo(name string) {
	golog.CopyStandardLogTo(name)
}

// Flush flushes all pending log I/O and calls Gobrake.Flush if
// Gobrake is not nil.
func Flush() {
	golog.Flush()
	if Gobrake != nil {
		Gobrake.Flush()
	}
}

// Info logs to the INFO log.
// Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
func Info(args ...interface{}) {
	golog.InfoDepth(1, args...)
	notifyAirbrake(infoLog, "", args...)
}

// Infoln logs to the INFO log.
// Arguments are handled in the manner of fmt.Println; a newline is appended if missing.
func Infoln(args ...interface{}) {
	golog.InfoDepth(1, fmt.Sprintln(args...))
	notifyAirbrake(infoLog, "", args...)
}

// Infof logs to the INFO log.
// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing.
func Infof(format string, args ...interface{}) {
	golog.InfoDepth(1, fmt.Sprintf(format, args...))
	notifyAirbrake(infoLog, format, args...)
}

// Warning logs to the WARNING and INFO logs.
// Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
func Warning(args ...interface{}) {
	golog.WarningDepth(1, args...)
	notifyAirbrake(warningLog, "", args...)
}

// Warningln logs to the WARNING and INFO logs.
// Arguments are handled in the manner of fmt.Println; a newline is appended if missing.
func Warningln(args ...interface{}) {
	golog.WarningDepth(1, fmt.Sprintln(args...))
	notifyAirbrake(warningLog, "", args...)
}

// Warningf logs to the WARNING and INFO logs.
// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing.
func Warningf(format string, args ...interface{}) {
	golog.WarningDepth(1, fmt.Sprintf(format, args...))
	notifyAirbrake(warningLog, format, args...)
}

// Error logs to the ERROR, WARNING, and INFO logs.
// Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
func Error(args ...interface{}) {
	golog.ErrorDepth(1, args...)
	notifyAirbrake(errorLog, "", args...)
}

// Errorln logs to the ERROR, WARNING, and INFO logs.
// Arguments are handled in the manner of fmt.Println; a newline is appended if missing.
func Errorln(args ...interface{}) {
	golog.ErrorDepth(1, fmt.Sprintln(args...))
	notifyAirbrake(errorLog, "", args...)
}

// Errorf logs to the ERROR, WARNING, and INFO logs.
// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing.
func Errorf(format string, args ...interface{}) {
	golog.ErrorDepth(1, fmt.Sprintf(format, args...))
	notifyAirbrake(errorLog, format, args...)
}

// Fatal logs to the FATAL, ERROR, WARNING, and INFO logs,
// including a stack trace of all running goroutines, then calls os.Exit(255).
// Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
func Fatal(args ...interface{}) {
	golog.FatalDepth(1, args...)
	notifyAirbrake(fatalLog, "", args...)
}

// Fatalln logs to the FATAL, ERROR, WARNING, and INFO logs,
// including a stack trace of all running goroutines, then calls os.Exit(255).
// Arguments are handled in the manner of fmt.Println; a newline is appended if missing.
func Fatalln(args ...interface{}) {
	golog.FatalDepth(1, fmt.Sprintln(args...))
	notifyAirbrake(fatalLog, "", args...)
}

// Fatalf logs to the FATAL, ERROR, WARNING, and INFO logs,
// including a stack trace of all running goroutines, then calls os.Exit(255).
// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing.
func Fatalf(format string, args ...interface{}) {
	golog.FatalDepth(1, fmt.Sprintf(format, args...))
	notifyAirbrake(fatalLog, format, args...)
}

// Exit logs to the FATAL, ERROR, WARNING, and INFO logs, then calls os.Exit(1).
// Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
func Exit(args ...interface{}) {
	golog.ExitDepth(1, args...)
	notifyAirbrake(errorLog, "", args...)
}

// Exitln logs to the FATAL, ERROR, WARNING, and INFO logs, then calls os.Exit(1).
func Exitln(args ...interface{}) {
	golog.ExitDepth(1, fmt.Sprintln(args...))
	notifyAirbrake(errorLog, "", args...)
}

// Exitf logs to the FATAL, ERROR, WARNING, and INFO logs, then calls os.Exit(1).
// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing.
func Exitf(format string, args ...interface{}) {
	golog.ExitDepth(1, fmt.Sprintf(format, args...))
	notifyAirbrake(errorLog, format, args...)
}

// Verbose is a boolean type that implements Infof (like Printf) etc.
// See the documentation of V for more information.
type Verbose bool

// V reports whether verbosity at the call site is at least the requested level.
// The returned value is a boolean of type Verbose, which implements Info, Infoln
// and Infof. These methods will write to the Info log if called.
// Thus, one may write either
//	if glog.V(2) { glog.Info("log this") }
// or
//	glog.V(2).Info("log this")
// The second form is shorter but the first is cheaper if logging is off because it does
// not evaluate its arguments.
//
// Whether an individual call to V generates a log record depends on the setting of
// the -v and --vmodule flags; both are off by default. If the level in the call to
// V is at least the value of -v, or of -vmodule for the source file containing the
// call, the V call will log.
func V(level golog.Level) Verbose {
	return Verbose(golog.V(level))
}

// Info is equivalent to the global Info function, guarded by the value of v.
// See the documentation of V for usage.
func (v Verbose) Info(args ...interface{}) {
	if v {
		Info(args...)
	}
}

// Infoln is equivalent to the global Infoln function, guarded by the value of v.
// See the documentation of V for usage.
func (v Verbose) Infoln(args ...interface{}) {
	if v {
		Infoln(args...)
	}
}

// Infof is equivalent to the global Infof function, guarded by the value of v.
// See the documentation of V for usage.
func (v Verbose) Infof(format string, args ...interface{}) {
	if v {
		Infof(format, args...)
	}
}
<|endoftext|>


<commit_before>package proxy

import (
	"net/http"
	"net/http/httputil"
	"net/url"
	"time"
)

func newHTTPProxy(target *url.URL, tr http.RoundTripper, flush time.Duration) http.Handler {
	return &httputil.ReverseProxy{
		// this is a simplified director function based on the
		// httputil.NewSingleHostReverseProxy() which does not
		// mangle the request and target URL since the target
		// URL is already in the correct format.
		Director: func(req *http.Request) {
			req.URL.Scheme = target.Scheme
			req.URL.Host = target.Host
			req.URL.Path = target.Path
			req.URL.RawQuery = target.RawQuery
			if _, ok := req.Header["User-Agent"]; !ok {
				// explicitly disable User-Agent so it's not set to default value
				req.Header.Set("User-Agent", "")
			}
		},
		FlushInterval: flush,
		Transport:     tr,
	}
}
<commit_msg>Handle context canceled errors + better http proxy error handling (#644)<commit_after>package proxy

import (
	"context"
	"io"
	"log"
	"net"
	"net/http"
	"net/http/httputil"
	"net/url"
	"time"
)

// StatusClientClosedRequest non-standard HTTP status code for client disconnection
const StatusClientClosedRequest = 499

func newHTTPProxy(target *url.URL, tr http.RoundTripper, flush time.Duration) http.Handler {
	return &httputil.ReverseProxy{
		// this is a simplified director function based on the
		// httputil.NewSingleHostReverseProxy() which does not
		// mangle the request and target URL since the target
		// URL is already in the correct format.
		Director: func(req *http.Request) {
			req.URL.Scheme = target.Scheme
			req.URL.Host = target.Host
			req.URL.Path = target.Path
			req.URL.RawQuery = target.RawQuery
			if _, ok := req.Header["User-Agent"]; !ok {
				// explicitly disable User-Agent so it's not set to default value
				req.Header.Set("User-Agent", "")
			}
		},
		FlushInterval: flush,
		Transport:     tr,
		ErrorHandler:  httpProxyErrorHandler,
	}
}

func httpProxyErrorHandler(w http.ResponseWriter, r *http.Request, err error) {
	// According to https://golang.org/src/net/http/httputil/reverseproxy.go#L74, Go will return a 502 (Bad Gateway) StatusCode by default if no ErroHandler is provided
	// If a "context canceled" error is returned by the http.Request handler this means the client closed the connection before getting a response
	// So we are changing the StatusCode on these situations to the non-standard 499 (Client Closed Request)

	statusCode := http.StatusInternalServerError

	if e, ok := err.(net.Error); ok {
		if e.Timeout() {
			statusCode = http.StatusGatewayTimeout
		} else {
			statusCode = http.StatusBadGateway
		}
	} else if err == io.EOF {
		statusCode = http.StatusBadGateway
	} else if err == context.Canceled {
		statusCode = StatusClientClosedRequest
	}

	w.WriteHeader(statusCode)
	// Theres nothing we can do if the client closes the connection and logging the "context canceled" errors will just add noise to the error log
	// Note: The access_log will still log the 499 response status codes
	if statusCode != StatusClientClosedRequest {
		log.Print("[ERROR] ", err)
	}

	return
}
<|endoftext|>


<commit_before>// Copyright 2017 ETH Zurich
//
// 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.

// Simple application for SCION connectivity using the snet library.
package main

import (
	"bufio"
	"flag"
	"fmt"
	"net"
	"os"
	"strconv"
	"time"

	log "github.com/inconshreveable/log15"
	"github.com/lucas-clemente/quic-go"
	"github.com/lucas-clemente/quic-go/qerr"

	"github.com/scionproto/scion/go/lib/addr"
	"github.com/scionproto/scion/go/lib/common"
	liblog "github.com/scionproto/scion/go/lib/log"
	sd "github.com/scionproto/scion/go/lib/sciond"
	"github.com/scionproto/scion/go/lib/snet"
	"github.com/scionproto/scion/go/lib/snet/squic"
	"github.com/scionproto/scion/go/lib/spath"
)

const (
	DefaultInterval = 1 * time.Second
	DefaultTimeout  = 2 * time.Second
	MaxPings        = 1 << 16
	ReqMsg          = "ping!" // ReqMsg and ReplyMsg length need to be the same
	ReplyMsg        = "pong!"
	TSLen           = 8
)

func GetDefaultSCIONDPath(ia addr.IA) string {
	return fmt.Sprintf("/run/shm/sciond/sd%s.sock", ia.FileFmt(false))
}

var (
	local       snet.Addr
	remote      snet.Addr
	interactive = flag.Bool("i", false, "Interactive mode")
	id          = flag.String("id", "pingpong", "Element ID")
	mode        = flag.String("mode", "client", "Run in client or server mode")
	sciond      = flag.String("sciond", "", "Path to sciond socket")
	dispatcher  = flag.String("dispatcher", "/run/shm/dispatcher/default.sock",
		"Path to dispatcher socket")
	count = flag.Int("count", 0,
		fmt.Sprintf("Number of pings, between 0 and %d; a count of 0 means infinity", MaxPings))
	timeout = flag.Duration("timeout", DefaultTimeout,
		"Timeout for the ping response")
	interval = flag.Duration("interval", DefaultInterval, "time between pings")
	verbose  = flag.Bool("v", false, "sets verbose output")
)

func init() {
	flag.Var((*snet.Addr)(&local), "local", "(Mandatory) address to listen on")
	flag.Var((*snet.Addr)(&remote), "remote", "(Mandatory for clients) address to connect to")
}

func main() {
	liblog.AddDefaultLogFlags()
	validateFlags()
	liblog.Setup(*id)
	defer liblog.LogPanicAndExit()
	switch *mode {
	case "client":
		if remote.Host == nil {
			LogFatal("Missing remote address")
		}
		if remote.L4Port == 0 {
			LogFatal("Invalid remote port", "remote port", remote.L4Port)
		}
		Client()
	case "server":
		Server()
	}
}

func validateFlags() {
	flag.Parse()
	if *mode != "client" && *mode != "server" {
		LogFatal("Unknown mode, must be either 'client' or 'server'")
	}
	if *mode == "client" && remote.Host == nil {
		LogFatal("Missing remote address")
	}
	if local.Host == nil {
		LogFatal("Missing local address")
	}
	if *sciond == "" {
		*sciond = GetDefaultSCIONDPath(local.IA)
	}
	if *count < 0 || *count > MaxPings {
		LogFatal("Invalid count", "min", 0, "max", MaxPings, "actual", *count)
	}
}

// Client dials to a remote SCION address and repeatedly sends ping messages
// while receiving pong messages. For each successful ping-pong, a message
// with the round trip time is printed. On errors (including timeouts),
// the Client exits.
func Client() {
	initNetwork()

	// Needs to happen before DialSCION, as it will 'copy' the remote to the connection.
	// If remote is not in local AS, we need a path!
	if !remote.IA.Eq(local.IA) {
		pathEntry := choosePath(*interactive)
		if pathEntry == nil {
			LogFatal("No paths available to remote destination")
		}
		remote.Path = spath.New(pathEntry.Path.FwdPath)
		remote.Path.InitOffsets()
		remote.NextHopHost = pathEntry.HostInfo.Host()
		remote.NextHopPort = pathEntry.HostInfo.Port
	}

	// Connect to remote address. Note that currently the SCION library
	// does not support automatic binding to local addresses, so the local
	// IP address needs to be supplied explicitly. When supplied a local
	// port of 0, DialSCION will assign a random free local port.
	qsess, err := squic.DialSCION(nil, &local, &remote)
	if err != nil {
		LogFatal("Unable to dial", "err", err)
	}
	defer qsess.Close(nil)

	qstream, err := qsess.OpenStreamSync()
	if err != nil {
		LogFatal("quic OpenStream failed", "err", err)
	}
	defer qstream.Close()
	log.Debug("Quic stream opened", "local", &local, "remote", &remote)
	go Send(qstream)
	Read(qstream)
}

func Send(qstream quic.Stream) {
	reqMsgLen := len(ReqMsg)
	payload := make([]byte, reqMsgLen+TSLen)
	copy(payload[0:], ReqMsg)
	for i := 0; i < *count || *count == 0; i++ {
		if i != 0 && *interval != 0 {
			time.Sleep(*interval)
		}

		// Send ping message to destination
		before := time.Now()
		common.Order.PutUint64(payload[reqMsgLen:], uint64(before.UnixNano()))
		written, err := qstream.Write(payload[:])
		if err != nil {
			qer := qerr.ToQuicError(err)
			if qer.ErrorCode == qerr.NetworkIdleTimeout {
				log.Debug("The connection timed out due to no network activity")
				break
			}
			log.Error("Unable to write", "err", err)
			continue
		}
		if written != len(ReqMsg)+TSLen {
			log.Error("Wrote incomplete message", "expected", len(ReqMsg)+TSLen,
				"actual", written)
			continue
		}
	}
	// After sending the last ping, set a ReadDeadline on the stream
	err := qstream.SetReadDeadline(time.Now().Add(*timeout))
	if err != nil {
		LogFatal("SetReadDeadline failed", "err", err)
	}
}

func Read(qstream quic.Stream) {
	// Receive pong message (with final timeout)
	b := make([]byte, 1<<12)
	replyMsgLen := len(ReplyMsg)
	for i := 0; i < *count || *count == 0; i++ {
		read, err := qstream.Read(b)
		after := time.Now()
		if err != nil {
			qer := qerr.ToQuicError(err)
			if qer.ErrorCode == qerr.PeerGoingAway {
				log.Debug("Quic peer disconnected")
				break
			}
			if nerr, ok := err.(net.Error); ok && nerr.Timeout() {
				log.Debug("ReadDeadline missed", "err", err)
				// ReadDeadline is only set after we are done writing
				// and we don't want to wait indefinitely for the remaining responses
				break
			}
			log.Error("Unable to read", "err", err)
			continue
		}
		if read < replyMsgLen || string(b[:replyMsgLen]) != ReplyMsg {
			fmt.Println("Received bad message", "expected", ReplyMsg,
				"actual", string(b[:read]))
			continue
		}
		if read < replyMsgLen+TSLen {
			fmt.Println("Received bad message missing timestamp",
				"actual", string(b[:read]))
			continue
		}
		before := time.Unix(0, int64(common.Order.Uint64(b[replyMsgLen:replyMsgLen+TSLen])))
		elapsed := after.Sub(before).Round(time.Microsecond)
		if *verbose {
			fmt.Printf("[%s]\tReceived %d bytes from %v: seq=%d RTT=%s\n",
				before.Format(common.TimeFmt), read, &remote, i, elapsed)
		} else {
			fmt.Printf("Received %d bytes from %v: seq=%d RTT=%s\n",
				read, &remote, i, elapsed)
		}
	}
}

// Server listens on a SCION address and replies to any ping message.
// On any error, the server exits.
func Server() {
	initNetwork()

	// Listen on SCION address
	qsock, err := squic.ListenSCION(nil, &local)
	if err != nil {
		LogFatal("Unable to listen", "err", err)
	}
	log.Debug("Listening", "local", qsock.Addr())
	for {
		qsess, err := qsock.Accept()
		if err != nil {
			log.Error("Unable to accept quic session", "err", err)
			continue
		}
		log.Debug("Quic session accepted", "src", qsess.RemoteAddr())
		go handleClient(qsess)
	}
}

func initNetwork() {
	// Initialize default SCION networking context
	if err := snet.Init(local.IA, *sciond, *dispatcher); err != nil {
		LogFatal("Unable to initialize SCION network", "err", err)
	}
	log.Debug("SCION network successfully initialized")
	if err := squic.Init("", ""); err != nil {
		LogFatal("Unable to initialize QUIC/SCION", "err", err)
	}
	log.Debug("QUIC/SCION successfully initialized")
}

func handleClient(qsess quic.Session) {
	defer qsess.Close(nil)
	qstream, err := qsess.AcceptStream()
	defer qstream.Close()
	if err != nil {
		log.Error("Unable to accept quic stream", "err", err)
		return
	}

	b := make([]byte, 1<<12)
	reqMsgLen := len(ReqMsg)
	for {
		// Receive ping message
		read, err := qstream.Read(b)
		if err != nil {
			qer := qerr.ToQuicError(err)
			if qer.ErrorCode == qerr.PeerGoingAway {
				log.Debug("Quic peer disconnected")
				break
			}
			log.Error("Unable to read", "err", err)
			break
		}
		if string(b[:reqMsgLen]) != ReqMsg {
			fmt.Println("Received bad message", "expected", ReqMsg,
				"actual", string(b[:reqMsgLen]), "full", string(b[:read]))
		}
		// extract timestamp
		ts := common.Order.Uint64(b[reqMsgLen:])

		// Send pong message
		replyMsgLen := len(ReplyMsg)
		copy(b[:replyMsgLen], ReplyMsg)
		common.Order.PutUint64(b[replyMsgLen:], ts)
		written, err := qstream.Write(b[:replyMsgLen+TSLen])
		if err != nil {
			log.Error("Unable to write", "err", err)
			continue
		} else if written != len(ReplyMsg)+TSLen {
			log.Error("Wrote incomplete message",
				"expected", len(ReplyMsg)+TSLen, "actual", written)
			continue
		}
	}
}

func LogFatal(msg string, a ...interface{}) {
	log.Crit(msg, a...)
	os.Exit(1)
}

func choosePath(interactive bool) *sd.PathReplyEntry {
	var paths []*sd.PathReplyEntry
	var pathIndex uint64

	pathMgr := snet.DefNetwork.PathResolver()
	pathSet := pathMgr.Query(local.IA, remote.IA)

	if len(pathSet) == 0 {
		return nil
	}
	for _, p := range pathSet {
		paths = append(paths, p.Entry)
	}
	if interactive {
		fmt.Printf("Available paths to %v\n", remote.IA)
		for i := range paths {
			fmt.Printf("[%2d] %s\n", i, paths[i].Path.String())
		}
		reader := bufio.NewReader(os.Stdin)
		for {
			fmt.Printf("Choose path: ")
			pathIndexStr, _ := reader.ReadString('\n')
			var err error
			pathIndex, err = strconv.ParseUint(pathIndexStr[:len(pathIndexStr)-1], 10, 64)
			if err == nil && int(pathIndex) < len(paths) {
				break
			}
			fmt.Fprintf(os.Stderr, "ERROR: Invalid path index, valid indices range: [0, %v]\n", len(paths))
		}
	}
	fmt.Printf("Using path:\n  %s\n", paths[pathIndex].Path.String())
	return paths[pathIndex]
}
<commit_msg>Quit pingpong when Accept returns error. (#1556)<commit_after>// Copyright 2017 ETH Zurich
//
// 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.

// Simple application for SCION connectivity using the snet library.
package main

import (
	"bufio"
	"flag"
	"fmt"
	"net"
	"os"
	"strconv"
	"time"

	log "github.com/inconshreveable/log15"
	"github.com/lucas-clemente/quic-go"
	"github.com/lucas-clemente/quic-go/qerr"

	"github.com/scionproto/scion/go/lib/addr"
	"github.com/scionproto/scion/go/lib/common"
	liblog "github.com/scionproto/scion/go/lib/log"
	sd "github.com/scionproto/scion/go/lib/sciond"
	"github.com/scionproto/scion/go/lib/snet"
	"github.com/scionproto/scion/go/lib/snet/squic"
	"github.com/scionproto/scion/go/lib/spath"
)

const (
	DefaultInterval = 1 * time.Second
	DefaultTimeout  = 2 * time.Second
	MaxPings        = 1 << 16
	ReqMsg          = "ping!" // ReqMsg and ReplyMsg length need to be the same
	ReplyMsg        = "pong!"
	TSLen           = 8
)

func GetDefaultSCIONDPath(ia addr.IA) string {
	return fmt.Sprintf("/run/shm/sciond/sd%s.sock", ia.FileFmt(false))
}

var (
	local       snet.Addr
	remote      snet.Addr
	interactive = flag.Bool("i", false, "Interactive mode")
	id          = flag.String("id", "pingpong", "Element ID")
	mode        = flag.String("mode", "client", "Run in client or server mode")
	sciond      = flag.String("sciond", "", "Path to sciond socket")
	dispatcher  = flag.String("dispatcher", "/run/shm/dispatcher/default.sock",
		"Path to dispatcher socket")
	count = flag.Int("count", 0,
		fmt.Sprintf("Number of pings, between 0 and %d; a count of 0 means infinity", MaxPings))
	timeout = flag.Duration("timeout", DefaultTimeout,
		"Timeout for the ping response")
	interval = flag.Duration("interval", DefaultInterval, "time between pings")
	verbose  = flag.Bool("v", false, "sets verbose output")
)

func init() {
	flag.Var((*snet.Addr)(&local), "local", "(Mandatory) address to listen on")
	flag.Var((*snet.Addr)(&remote), "remote", "(Mandatory for clients) address to connect to")
}

func main() {
	liblog.AddDefaultLogFlags()
	validateFlags()
	liblog.Setup(*id)
	defer liblog.LogPanicAndExit()
	switch *mode {
	case "client":
		if remote.Host == nil {
			LogFatal("Missing remote address")
		}
		if remote.L4Port == 0 {
			LogFatal("Invalid remote port", "remote port", remote.L4Port)
		}
		Client()
	case "server":
		Server()
	}
}

func validateFlags() {
	flag.Parse()
	if *mode != "client" && *mode != "server" {
		LogFatal("Unknown mode, must be either 'client' or 'server'")
	}
	if *mode == "client" && remote.Host == nil {
		LogFatal("Missing remote address")
	}
	if local.Host == nil {
		LogFatal("Missing local address")
	}
	if *sciond == "" {
		*sciond = GetDefaultSCIONDPath(local.IA)
	}
	if *count < 0 || *count > MaxPings {
		LogFatal("Invalid count", "min", 0, "max", MaxPings, "actual", *count)
	}
}

// Client dials to a remote SCION address and repeatedly sends ping messages
// while receiving pong messages. For each successful ping-pong, a message
// with the round trip time is printed. On errors (including timeouts),
// the Client exits.
func Client() {
	initNetwork()

	// Needs to happen before DialSCION, as it will 'copy' the remote to the connection.
	// If remote is not in local AS, we need a path!
	if !remote.IA.Eq(local.IA) {
		pathEntry := choosePath(*interactive)
		if pathEntry == nil {
			LogFatal("No paths available to remote destination")
		}
		remote.Path = spath.New(pathEntry.Path.FwdPath)
		remote.Path.InitOffsets()
		remote.NextHopHost = pathEntry.HostInfo.Host()
		remote.NextHopPort = pathEntry.HostInfo.Port
	}

	// Connect to remote address. Note that currently the SCION library
	// does not support automatic binding to local addresses, so the local
	// IP address needs to be supplied explicitly. When supplied a local
	// port of 0, DialSCION will assign a random free local port.
	qsess, err := squic.DialSCION(nil, &local, &remote)
	if err != nil {
		LogFatal("Unable to dial", "err", err)
	}
	defer qsess.Close(nil)

	qstream, err := qsess.OpenStreamSync()
	if err != nil {
		LogFatal("quic OpenStream failed", "err", err)
	}
	defer qstream.Close()
	log.Debug("Quic stream opened", "local", &local, "remote", &remote)
	go Send(qstream)
	Read(qstream)
}

func Send(qstream quic.Stream) {
	reqMsgLen := len(ReqMsg)
	payload := make([]byte, reqMsgLen+TSLen)
	copy(payload[0:], ReqMsg)
	for i := 0; i < *count || *count == 0; i++ {
		if i != 0 && *interval != 0 {
			time.Sleep(*interval)
		}

		// Send ping message to destination
		before := time.Now()
		common.Order.PutUint64(payload[reqMsgLen:], uint64(before.UnixNano()))
		written, err := qstream.Write(payload[:])
		if err != nil {
			qer := qerr.ToQuicError(err)
			if qer.ErrorCode == qerr.NetworkIdleTimeout {
				log.Debug("The connection timed out due to no network activity")
				break
			}
			log.Error("Unable to write", "err", err)
			continue
		}
		if written != len(ReqMsg)+TSLen {
			log.Error("Wrote incomplete message", "expected", len(ReqMsg)+TSLen,
				"actual", written)
			continue
		}
	}
	// After sending the last ping, set a ReadDeadline on the stream
	err := qstream.SetReadDeadline(time.Now().Add(*timeout))
	if err != nil {
		LogFatal("SetReadDeadline failed", "err", err)
	}
}

func Read(qstream quic.Stream) {
	// Receive pong message (with final timeout)
	b := make([]byte, 1<<12)
	replyMsgLen := len(ReplyMsg)
	for i := 0; i < *count || *count == 0; i++ {
		read, err := qstream.Read(b)
		after := time.Now()
		if err != nil {
			qer := qerr.ToQuicError(err)
			if qer.ErrorCode == qerr.PeerGoingAway {
				log.Debug("Quic peer disconnected")
				break
			}
			if nerr, ok := err.(net.Error); ok && nerr.Timeout() {
				log.Debug("ReadDeadline missed", "err", err)
				// ReadDeadline is only set after we are done writing
				// and we don't want to wait indefinitely for the remaining responses
				break
			}
			log.Error("Unable to read", "err", err)
			continue
		}
		if read < replyMsgLen || string(b[:replyMsgLen]) != ReplyMsg {
			fmt.Println("Received bad message", "expected", ReplyMsg,
				"actual", string(b[:read]))
			continue
		}
		if read < replyMsgLen+TSLen {
			fmt.Println("Received bad message missing timestamp",
				"actual", string(b[:read]))
			continue
		}
		before := time.Unix(0, int64(common.Order.Uint64(b[replyMsgLen:replyMsgLen+TSLen])))
		elapsed := after.Sub(before).Round(time.Microsecond)
		if *verbose {
			fmt.Printf("[%s]\tReceived %d bytes from %v: seq=%d RTT=%s\n",
				before.Format(common.TimeFmt), read, &remote, i, elapsed)
		} else {
			fmt.Printf("Received %d bytes from %v: seq=%d RTT=%s\n",
				read, &remote, i, elapsed)
		}
	}
}

// Server listens on a SCION address and replies to any ping message.
// On any error, the server exits.
func Server() {
	initNetwork()

	// Listen on SCION address
	qsock, err := squic.ListenSCION(nil, &local)
	if err != nil {
		LogFatal("Unable to listen", "err", err)
	}
	log.Debug("Listening", "local", qsock.Addr())
	for {
		qsess, err := qsock.Accept()
		if err != nil {
			log.Error("Unable to accept quic session", "err", err)
			// Accept failing means the socket is unusable.
			break
		}
		log.Debug("Quic session accepted", "src", qsess.RemoteAddr())
		go handleClient(qsess)
	}
}

func initNetwork() {
	// Initialize default SCION networking context
	if err := snet.Init(local.IA, *sciond, *dispatcher); err != nil {
		LogFatal("Unable to initialize SCION network", "err", err)
	}
	log.Debug("SCION network successfully initialized")
	if err := squic.Init("", ""); err != nil {
		LogFatal("Unable to initialize QUIC/SCION", "err", err)
	}
	log.Debug("QUIC/SCION successfully initialized")
}

func handleClient(qsess quic.Session) {
	defer qsess.Close(nil)
	qstream, err := qsess.AcceptStream()
	if err != nil {
		log.Error("Unable to accept quic stream", "err", err)
		return
	}
	defer qstream.Close()

	b := make([]byte, 1<<12)
	reqMsgLen := len(ReqMsg)
	for {
		// Receive ping message
		read, err := qstream.Read(b)
		if err != nil {
			qer := qerr.ToQuicError(err)
			if qer.ErrorCode == qerr.PeerGoingAway {
				log.Debug("Quic peer disconnected")
				break
			}
			log.Error("Unable to read", "err", err)
			break
		}
		if string(b[:reqMsgLen]) != ReqMsg {
			fmt.Println("Received bad message", "expected", ReqMsg,
				"actual", string(b[:reqMsgLen]), "full", string(b[:read]))
		}
		// extract timestamp
		ts := common.Order.Uint64(b[reqMsgLen:])

		// Send pong message
		replyMsgLen := len(ReplyMsg)
		copy(b[:replyMsgLen], ReplyMsg)
		common.Order.PutUint64(b[replyMsgLen:], ts)
		written, err := qstream.Write(b[:replyMsgLen+TSLen])
		if err != nil {
			log.Error("Unable to write", "err", err)
			continue
		} else if written != len(ReplyMsg)+TSLen {
			log.Error("Wrote incomplete message",
				"expected", len(ReplyMsg)+TSLen, "actual", written)
			continue
		}
	}
}

func LogFatal(msg string, a ...interface{}) {
	log.Crit(msg, a...)
	os.Exit(1)
}

func choosePath(interactive bool) *sd.PathReplyEntry {
	var paths []*sd.PathReplyEntry
	var pathIndex uint64

	pathMgr := snet.DefNetwork.PathResolver()
	pathSet := pathMgr.Query(local.IA, remote.IA)

	if len(pathSet) == 0 {
		return nil
	}
	for _, p := range pathSet {
		paths = append(paths, p.Entry)
	}
	if interactive {
		fmt.Printf("Available paths to %v\n", remote.IA)
		for i := range paths {
			fmt.Printf("[%2d] %s\n", i, paths[i].Path.String())
		}
		reader := bufio.NewReader(os.Stdin)
		for {
			fmt.Printf("Choose path: ")
			pathIndexStr, _ := reader.ReadString('\n')
			var err error
			pathIndex, err = strconv.ParseUint(pathIndexStr[:len(pathIndexStr)-1], 10, 64)
			if err == nil && int(pathIndex) < len(paths) {
				break
			}
			fmt.Fprintf(os.Stderr, "ERROR: Invalid path index, valid indices range: [0, %v]\n", len(paths))
		}
	}
	fmt.Printf("Using path:\n  %s\n", paths[pathIndex].Path.String())
	return paths[pathIndex]
}
<|endoftext|>


<commit_before>// Copyright 2016 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package dockermachine

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"

	"github.com/docker/machine/libmachine"
	"github.com/docker/machine/libmachine/drivers"
	"github.com/docker/machine/libmachine/drivers/rpc"
	"github.com/tsuru/tsuru/iaas"
)

type DockerMachine struct {
	io.Closer
	client libmachine.API
	config *DockerMachineConfig
	path   string
}

type DockerMachineConfig struct {
	CaPath                 string
	InsecureRegistry       string
	DockerEngineInstallURL string
}

type dockerMachineAPI interface {
	io.Closer
	CreateMachine(string, string, map[string]interface{}) (*iaas.Machine, error)
	DeleteMachine(*iaas.Machine) error
}

func NewDockerMachine(config DockerMachineConfig) (dockerMachineAPI, error) {
	path, err := ioutil.TempDir("", "")
	if err != nil {
		return nil, err
	}
	if config.CaPath != "" {
		err = copy(filepath.Join(config.CaPath, "ca.pem"), filepath.Join(path, "ca.pem"))
		if err != nil {
			return nil, err
		}
		err = copy(filepath.Join(config.CaPath, "ca-key.pem"), filepath.Join(path, "ca-key.pem"))
		if err != nil {
			return nil, err
		}
	}
	return &DockerMachine{
		path:   path,
		client: libmachine.NewClient(path, path),
		config: &config,
	}, nil
}

func (d *DockerMachine) Close() error {
	os.RemoveAll(d.path)
	return d.client.Close()
}

func (d *DockerMachine) CreateMachine(name, driver string, params map[string]interface{}) (*iaas.Machine, error) {
	rawDriver, err := json.Marshal(&drivers.BaseDriver{
		MachineName: name,
		StorePath:   d.path,
	})
	if err != nil {
		return nil, err
	}
	host, err := d.client.NewHost(driver, rawDriver)
	if err != nil {
		return nil, err
	}
	err = configureDriver(host.Driver, params)
	if err != nil {
		return nil, err
	}
	engineOpts := host.HostOptions.EngineOptions
	if d.config.InsecureRegistry != "" {
		engineOpts.InsecureRegistry = []string{d.config.InsecureRegistry}
	}
	if d.config.DockerEngineInstallURL != "" {
		engineOpts.InstallURL = d.config.DockerEngineInstallURL
	}
	err = d.client.Create(host)
	if err != nil {
		return nil, err
	}
	ip, err := host.Driver.GetIP()
	if err != nil {
		return nil, err
	}
	rawDriver, err = json.Marshal(host.Driver)
	if err != nil {
		return nil, err
	}
	var driverData map[string]interface{}
	err = json.Unmarshal(rawDriver, &driverData)
	if err != nil {
		return nil, err
	}
	m := &iaas.Machine{
		Id:         host.Name,
		Address:    ip,
		Port:       2376,
		Protocol:   "https",
		CustomData: driverData,
	}
	if host.AuthOptions() != nil {
		m.CaCert, err = ioutil.ReadFile(host.AuthOptions().CaCertPath)
		if err != nil {
			return nil, err
		}
		m.ClientCert, err = ioutil.ReadFile(host.AuthOptions().ClientCertPath)
		if err != nil {
			return nil, err
		}
		m.ClientKey, err = ioutil.ReadFile(host.AuthOptions().ClientKeyPath)
		if err != nil {
			return nil, err
		}
	}
	return m, nil
}

func (d *DockerMachine) DeleteMachine(m *iaas.Machine) error {
	rawDriver, err := json.Marshal(m.CustomData)
	if err != nil {
		return err
	}
	host, err := d.client.NewHost(m.CreationParams["driver"], rawDriver)
	if err != nil {
		return err
	}
	err = host.Driver.Remove()
	if err != nil {
		return err
	}
	return d.client.Remove(m.Id)
}

func configureDriver(driver drivers.Driver, driverOpts map[string]interface{}) error {
	opts := &rpcdriver.RPCFlags{Values: driverOpts}
	for _, c := range driver.GetCreateFlags() {
		_, ok := opts.Values[c.String()]
		if !ok {
			opts.Values[c.String()] = c.Default()
			if c.Default() == nil {
				opts.Values[c.String()] = false
			}
		}
	}
	if err := driver.SetConfigFromFlags(opts); err != nil {
		return fmt.Errorf("Error setting driver configurations: %s", err)
	}
	return nil
}

func copy(src, dst string) error {
	fileSrc, err := ioutil.ReadFile(src)
	if err != nil {
		return err
	}
	return ioutil.WriteFile(dst, fileSrc, 0644)
}
<commit_msg>iaas/dockermachine: discards dockermachine logs<commit_after>// Copyright 2016 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package dockermachine

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"

	"github.com/docker/machine/libmachine"
	"github.com/docker/machine/libmachine/drivers"
	"github.com/docker/machine/libmachine/drivers/rpc"
	"github.com/docker/machine/libmachine/log"
	"github.com/tsuru/tsuru/iaas"
)

var defaultWriter = ioutil.Discard

type DockerMachine struct {
	io.Closer
	client libmachine.API
	config *DockerMachineConfig
	path   string
}

type DockerMachineConfig struct {
	CaPath                 string
	InsecureRegistry       string
	DockerEngineInstallURL string
	OutWriter              io.Writer
	ErrWriter              io.Writer
}

type dockerMachineAPI interface {
	io.Closer
	CreateMachine(string, string, map[string]interface{}) (*iaas.Machine, error)
	DeleteMachine(*iaas.Machine) error
}

func NewDockerMachine(config DockerMachineConfig) (dockerMachineAPI, error) {
	path, err := ioutil.TempDir("", "")
	if err != nil {
		return nil, err
	}
	if config.CaPath != "" {
		err = copy(filepath.Join(config.CaPath, "ca.pem"), filepath.Join(path, "ca.pem"))
		if err != nil {
			return nil, err
		}
		err = copy(filepath.Join(config.CaPath, "ca-key.pem"), filepath.Join(path, "ca-key.pem"))
		if err != nil {
			return nil, err
		}
	}
	if config.OutWriter != nil {
		log.SetOutWriter(config.OutWriter)
	} else {
		log.SetOutWriter(defaultWriter)
	}
	if config.ErrWriter != nil {
		log.SetOutWriter(config.ErrWriter)
	} else {
		log.SetOutWriter(defaultWriter)
	}
	return &DockerMachine{
		path:   path,
		client: libmachine.NewClient(path, path),
		config: &config,
	}, nil
}

func (d *DockerMachine) Close() error {
	os.RemoveAll(d.path)
	return d.client.Close()
}

func (d *DockerMachine) CreateMachine(name, driver string, params map[string]interface{}) (*iaas.Machine, error) {
	rawDriver, err := json.Marshal(&drivers.BaseDriver{
		MachineName: name,
		StorePath:   d.path,
	})
	if err != nil {
		return nil, err
	}
	host, err := d.client.NewHost(driver, rawDriver)
	if err != nil {
		return nil, err
	}
	err = configureDriver(host.Driver, params)
	if err != nil {
		return nil, err
	}
	engineOpts := host.HostOptions.EngineOptions
	if d.config.InsecureRegistry != "" {
		engineOpts.InsecureRegistry = []string{d.config.InsecureRegistry}
	}
	if d.config.DockerEngineInstallURL != "" {
		engineOpts.InstallURL = d.config.DockerEngineInstallURL
	}
	err = d.client.Create(host)
	if err != nil {
		return nil, err
	}
	ip, err := host.Driver.GetIP()
	if err != nil {
		return nil, err
	}
	rawDriver, err = json.Marshal(host.Driver)
	if err != nil {
		return nil, err
	}
	var driverData map[string]interface{}
	err = json.Unmarshal(rawDriver, &driverData)
	if err != nil {
		return nil, err
	}
	m := &iaas.Machine{
		Id:         host.Name,
		Address:    ip,
		Port:       2376,
		Protocol:   "https",
		CustomData: driverData,
	}
	if host.AuthOptions() != nil {
		m.CaCert, err = ioutil.ReadFile(host.AuthOptions().CaCertPath)
		if err != nil {
			return nil, err
		}
		m.ClientCert, err = ioutil.ReadFile(host.AuthOptions().ClientCertPath)
		if err != nil {
			return nil, err
		}
		m.ClientKey, err = ioutil.ReadFile(host.AuthOptions().ClientKeyPath)
		if err != nil {
			return nil, err
		}
	}
	return m, nil
}

func (d *DockerMachine) DeleteMachine(m *iaas.Machine) error {
	rawDriver, err := json.Marshal(m.CustomData)
	if err != nil {
		return err
	}
	host, err := d.client.NewHost(m.CreationParams["driver"], rawDriver)
	if err != nil {
		return err
	}
	err = host.Driver.Remove()
	if err != nil {
		return err
	}
	return d.client.Remove(m.Id)
}

func configureDriver(driver drivers.Driver, driverOpts map[string]interface{}) error {
	opts := &rpcdriver.RPCFlags{Values: driverOpts}
	for _, c := range driver.GetCreateFlags() {
		_, ok := opts.Values[c.String()]
		if !ok {
			opts.Values[c.String()] = c.Default()
			if c.Default() == nil {
				opts.Values[c.String()] = false
			}
		}
	}
	if err := driver.SetConfigFromFlags(opts); err != nil {
		return fmt.Errorf("Error setting driver configurations: %s", err)
	}
	return nil
}

func copy(src, dst string) error {
	fileSrc, err := ioutil.ReadFile(src)
	if err != nil {
		return err
	}
	return ioutil.WriteFile(dst, fileSrc, 0644)
}
<|endoftext|>


<commit_before>
<commit_msg>Removed uneeded file<commit_after><|endoftext|>


<commit_before>/*
 * Copyright (C) 2016 Red Hat, Inc.
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 (
	"fmt"

	"github.com/skydive-project/skydive/api/types"
	"github.com/skydive-project/skydive/common"
	"github.com/skydive-project/skydive/flow"
	ge "github.com/skydive-project/skydive/gremlin/traversal"
	shttp "github.com/skydive-project/skydive/http"
	"github.com/skydive-project/skydive/logging"
	"github.com/skydive-project/skydive/topology/graph"
)

// CaptureResourceHandler describes a capture ressouce handler
type CaptureResourceHandler struct {
	ResourceHandler
}

// CaptureAPIHandler based on BasicAPIHandler
type CaptureAPIHandler struct {
	BasicAPIHandler
	Graph *graph.Graph
}

// Name returns "capture"
func (c *CaptureResourceHandler) Name() string {
	return "capture"
}

// New creates a new capture resource
func (c *CaptureResourceHandler) New() types.Resource {
	return &types.Capture{
		LayerKeyMode: flow.DefaultLayerKeyModeName(),
	}
}

// Decorate populates the capture resource
func (c *CaptureAPIHandler) Decorate(resource types.Resource) {
	capture := resource.(*types.Capture)

	count := 0
	pcapSocket := ""

	c.Graph.RLock()
	defer c.Graph.RUnlock()

	res, err := ge.TopologyGremlinQuery(c.Graph, capture.GremlinQuery)
	if err != nil {
		logging.GetLogger().Errorf("Gremlin error: %s", err.Error())
		return
	}

	for _, value := range res.Values() {
		switch value.(type) {
		case *graph.Node:
			n := value.(*graph.Node)
			if cuuid, _ := n.GetFieldString("Capture.ID"); cuuid != "" {
				count++
			}
			if p, _ := n.GetFieldString("Capture.PCAPSocket"); p != "" {
				pcapSocket = p
			}
		case []*graph.Node:
			for _, n := range value.([]*graph.Node) {
				if cuuid, _ := n.GetFieldString("Capture.ID"); cuuid != "" {
					count++
				}
				if p, _ := n.GetFieldString("Capture.PCAPSocket"); p != "" {
					pcapSocket = p
				}
			}
		default:
			count = 0
		}
	}

	capture.Count = count
	capture.PCAPSocket = pcapSocket
}

// Create tests that resource GremlinQuery does not exists already
func (c *CaptureAPIHandler) Create(r types.Resource) error {
	capture := r.(*types.Capture)

	// check capabilities
	if capture.Type != "" {
		if capture.BPFFilter != "" {
			if !common.CheckProbeCapabilities(capture.Type, common.BPFCapability) {
				return fmt.Errorf("%s capture doesn't support BPF filtering", capture.Type)
			}
		}
		if capture.RawPacketLimit != 0 {
			if !common.CheckProbeCapabilities(capture.Type, common.RawPacketsCapability) {
				return fmt.Errorf("%s capture doesn't support raw packet capture", capture.Type)
			}
		}
		if capture.ExtraTCPMetric {
			if !common.CheckProbeCapabilities(capture.Type, common.ExtraTCPMetricCapability) {
				return fmt.Errorf("%s capture doesn't support extra TCP metrics capture", capture.Type)
			}
		}
	}

	resources := c.Index()
	for _, resource := range resources {
		resource := resource.(*types.Capture)
		if resource.GremlinQuery == capture.GremlinQuery {
			return fmt.Errorf("Duplicate capture, uuid=%s", capture.UUID)
		}
	}

	return c.BasicAPIHandler.Create(r)
}

// RegisterCaptureAPI registers an new resource, capture
func RegisterCaptureAPI(apiServer *Server, g *graph.Graph, authBackend shttp.AuthenticationBackend) (*CaptureAPIHandler, error) {
	captureAPIHandler := &CaptureAPIHandler{
		BasicAPIHandler: BasicAPIHandler{
			ResourceHandler: &CaptureResourceHandler{},
			EtcdKeyAPI:      apiServer.EtcdKeyAPI,
		},
		Graph: g,
	}
	if err := apiServer.RegisterAPIHandler(captureAPIHandler, authBackend); err != nil {
		return nil, err
	}
	return captureAPIHandler, nil
}
<commit_msg>capture: count only active capture<commit_after>/*
 * Copyright (C) 2016 Red Hat, Inc.
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 (
	"fmt"

	"github.com/skydive-project/skydive/api/types"
	"github.com/skydive-project/skydive/common"
	"github.com/skydive-project/skydive/flow"
	ge "github.com/skydive-project/skydive/gremlin/traversal"
	shttp "github.com/skydive-project/skydive/http"
	"github.com/skydive-project/skydive/logging"
	"github.com/skydive-project/skydive/topology/graph"
)

// CaptureResourceHandler describes a capture ressouce handler
type CaptureResourceHandler struct {
	ResourceHandler
}

// CaptureAPIHandler based on BasicAPIHandler
type CaptureAPIHandler struct {
	BasicAPIHandler
	Graph *graph.Graph
}

// Name returns "capture"
func (c *CaptureResourceHandler) Name() string {
	return "capture"
}

// New creates a new capture resource
func (c *CaptureResourceHandler) New() types.Resource {
	return &types.Capture{
		LayerKeyMode: flow.DefaultLayerKeyModeName(),
	}
}

// Decorate populates the capture resource
func (c *CaptureAPIHandler) Decorate(resource types.Resource) {
	capture := resource.(*types.Capture)

	count := 0
	pcapSocket := ""

	c.Graph.RLock()
	defer c.Graph.RUnlock()

	res, err := ge.TopologyGremlinQuery(c.Graph, capture.GremlinQuery)
	if err != nil {
		logging.GetLogger().Errorf("Gremlin error: %s", err)
		return
	}

	for _, value := range res.Values() {
		switch value.(type) {
		case *graph.Node:
			n := value.(*graph.Node)
			if state, _ := n.GetFieldString("Capture.State"); state == "active" {
				count++
			}
			if p, _ := n.GetFieldString("Capture.PCAPSocket"); p != "" {
				pcapSocket = p
			}
		case []*graph.Node:
			for _, n := range value.([]*graph.Node) {
				if cuuid, _ := n.GetFieldString("Capture.ID"); cuuid != "" {
					count++
				}
				if p, _ := n.GetFieldString("Capture.PCAPSocket"); p != "" {
					pcapSocket = p
				}
			}
		default:
			count = 0
		}
	}

	capture.Count = count
	capture.PCAPSocket = pcapSocket
}

// Create tests that resource GremlinQuery does not exists already
func (c *CaptureAPIHandler) Create(r types.Resource) error {
	capture := r.(*types.Capture)

	// check capabilities
	if capture.Type != "" {
		if capture.BPFFilter != "" {
			if !common.CheckProbeCapabilities(capture.Type, common.BPFCapability) {
				return fmt.Errorf("%s capture doesn't support BPF filtering", capture.Type)
			}
		}
		if capture.RawPacketLimit != 0 {
			if !common.CheckProbeCapabilities(capture.Type, common.RawPacketsCapability) {
				return fmt.Errorf("%s capture doesn't support raw packet capture", capture.Type)
			}
		}
		if capture.ExtraTCPMetric {
			if !common.CheckProbeCapabilities(capture.Type, common.ExtraTCPMetricCapability) {
				return fmt.Errorf("%s capture doesn't support extra TCP metrics capture", capture.Type)
			}
		}
	}

	resources := c.Index()
	for _, resource := range resources {
		resource := resource.(*types.Capture)
		if resource.GremlinQuery == capture.GremlinQuery {
			return fmt.Errorf("Duplicate capture, uuid=%s", capture.UUID)
		}
	}

	return c.BasicAPIHandler.Create(r)
}

// RegisterCaptureAPI registers an new resource, capture
func RegisterCaptureAPI(apiServer *Server, g *graph.Graph, authBackend shttp.AuthenticationBackend) (*CaptureAPIHandler, error) {
	captureAPIHandler := &CaptureAPIHandler{
		BasicAPIHandler: BasicAPIHandler{
			ResourceHandler: &CaptureResourceHandler{},
			EtcdKeyAPI:      apiServer.EtcdKeyAPI,
		},
		Graph: g,
	}
	if err := apiServer.RegisterAPIHandler(captureAPIHandler, authBackend); err != nil {
		return nil, err
	}
	return captureAPIHandler, nil
}
<|endoftext|>


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

package parts

import (
	"bytes"
	"text/template"
)

const multiplexerTmplSrc = `var mxwg sync.WaitGroup
{{range $.Inputs}}
mxwg.Add(1)
go func() {
    for x := range {{.}} {
        {{$.Output}} <- x
    }
    mxwg.Done()
}()
{{end}}
mxwg.Wait()
close({{$.Output}})
`

var multiplexerTmpl = template.Must(template.New("multiplexer").Parse(multiplexerTmplSrc))

// Multiplexer reads from N input channels and writes values into a single output
// channel. All the channels must have the same or compatible types. Once all input
// channels are closed, the output channel is also closed.
type Multiplexer struct {
	Inputs []string
	Output string
}

// Impl returns the content of a goroutine implementing the multiplexer.
func (m *Multiplexer) Impl() string {
	b := new(bytes.Buffer)
	multiplexerTmpl.Execute(b, m)
	return b.String()
}

// ChannelsRead returns the names of all channels read by this goroutine.
func (m *Multiplexer) ChannelsRead() []string { return m.Inputs }

// ChannelsWritten returns the names of all channels written by this goroutine.
func (m *Multiplexer) ChannelsWritten() []string { return []string{m.Output} }
<commit_msg>Mux implements Part<commit_after>// Copyright 2016 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package parts

import (
	"bytes"
	"text/template"

	"shenzhen-go/graph"
)

const multiplexerTmplSrc = `var mxwg sync.WaitGroup
{{range $.Inputs}}
mxwg.Add(1)
go func() {
    for x := range {{.}} {
        {{$.Output}} <- x
    }
    mxwg.Done()
}()
{{end}}
mxwg.Wait()
close({{$.Output}})
`

var (
	multiplexerTmpl = template.Must(template.New("multiplexer").Parse(multiplexerTmplSrc))

	// While being developed, check the interface is matched.
	_ = graph.Part(&Multiplexer{})
)

// Multiplexer reads from N input channels and writes values into a single output
// channel. All the channels must have the same or compatible types. Once all input
// channels are closed, the output channel is also closed.
type Multiplexer struct {
	Inputs []string
	Output string
}

// Channels returns the names of all channels used by this goroutine.
func (m *Multiplexer) Channels() (read, written []string) { return m.Inputs, []string{m.Output} }

// Impl returns the content of a goroutine implementing the multiplexer.
func (m *Multiplexer) Impl() string {
	b := new(bytes.Buffer)
	multiplexerTmpl.Execute(b, m)
	return b.String()
}

// Refresh refreshes any cached information.
func (m *Multiplexer) Refresh(g *graph.Graph) error {
	return nil
}
<|endoftext|>


<commit_before>package adapter

import (
	"reflect"
	"text/template"
)

const (
	PlanStandalone     = "standalone"
	PlanReplicaSet     = "replica_set"
	PlanShardedCluster = "sharded_cluster"
)

var plans = map[string]*template.Template{}

func init() {
	funcs := template.FuncMap{
		"last": func(a interface{}, x int) bool {
			return reflect.ValueOf(a).Len()-1 == x
		},
	}

	var err error
	for k, s := range plansRaw {
		plans[k], err = template.New(string(k)).Funcs(funcs).Parse(s)
		if err != nil {
			panic(err)
		}
	}
}

var plansRaw = map[string]string{
	PlanStandalone: `{
    "options": {
        "downloadBase": "/var/lib/mongodb-mms-automation",
    },
    "mongoDbVersions": [
        {"name": "{{.Version}}"}
    ],
    "backupVersions": [{
        "hostname": "{{index .Nodes 0}}",
        "logPath": "/var/vcap/sys/log/mongod_node/backup-agent.log",
        "logRotate": {
            "sizeThresholdMB": 1000,
            "timeThresholdHrs": 24
        }
    }],
    "monitoringVersions": [{
        "hostname": "{{index .Nodes 0}}",
        "logPath": "/var/vcap/sys/log/mongod_node/monitoring-agent.log",
        "logRotate": {
            "sizeThresholdMB": 1000,
            "timeThresholdHrs": 24
        }
    }],
    "processes": [{
        "args2_6": {
            "net": {
                "port": 28000
            },
            "storage": {
                "dbPath": "/var/vcap/store/mongodb-data"
            },
            "systemLog": {
                "destination": "file",
                "path": "/var/vcap/sys/log/mongod_node/mongodb.log"
            }
        },
        "hostname": "{{index .Nodes 0}}",
        "logRotate": {
            "sizeThresholdMB": 1000,
            "timeThresholdHrs": 24
        },
        "name": "{{index .Nodes 0}}",
        "processType": "mongod",
        "version": "{{.Version}}",
        "authSchemaVersion": 5
    }],
    "replicaSets": [],
    "roles": [],
    "sharding": [],

    "auth": {
        "autoUser": "mms-automation",
        "autoPwd": "{{.Password}}",
        "deploymentAuthMechanisms": [
            "SCRAM-SHA-1"
        ],
        "key": "{{.Key}}",
        "keyfile": "/var/vcap/jobs/mongod_node/config/mongo_om.key",
        "disabled": false,
        "usersDeleted": [],
        "usersWanted": [
            {
                "db": "admin",
                "roles": [
                    {
                        "db": "admin",
                        "role": "clusterMonitor"
                    }
                ],
                "user": "mms-monitoring-agent",
                "initPwd": "{{.Password}}"
            },
            {
                "db": "admin",
                "roles": [
                    {
                        "db": "admin",
                        "role": "clusterAdmin"
                    },
                    {
                        "db": "admin",
                        "role": "readAnyDatabase"
                    },
                    {
                        "db": "admin",
                        "role": "userAdminAnyDatabase"
                    },
                    {
                        "db": "local",
                        "role": "readWrite"
                    },
                    {
                        "db": "admin",
                        "role": "readWrite"
                    }
                ],
                "user": "mms-backup-agent",
                "initPwd": "{{.Password}}"
            },
            {
               "db": "admin" ,
               "user": "admin" ,
               "roles": [
                 {
                     "db": "admin",
                     "role": "clusterAdmin"
                 },
                 {
                     "db": "admin",
                     "role": "readAnyDatabase"
                 },
                 {
                     "db": "admin",
                     "role": "userAdminAnyDatabase"
                 },
                 {
                     "db": "local",
                     "role": "readWrite"
                 },
                 {
                     "db": "admin",
                     "role": "readWrite"
                 }
               ],
               "initPwd": "{{.AdminPassword}}"
            }
        ],
        "autoAuthMechanism": "SCRAM-SHA-1"
    }
}`,

	PlanShardedCluster: `{
    "options": {
        "downloadBase": "/var/lib/mongodb-mms-automation",
    },
    "mongoDbVersions": [
        {"name": "{{.Version}}"}
    ],
    "backupVersions": [
    ],
    "monitoringVersions": [{
        "hostname": "{{index .Cluster.Routers 0}}",
        "logPath": "/var/vcap/sys/log/mongod_node/monitoring-agent.log",
        "logRotate": {
            "sizeThresholdMB": 1000,
            "timeThresholdHrs": 24
        }
    }],
    "processes": [
      {{range $i, $node := .Cluster.Routers}}{
          "args2_6": {
              "net": {
                  "port": 28000
              },
              "systemLog": {
                  "destination": "file",
                  "path": "/var/vcap/sys/log/mongod_node/mongodb.log"
              }
          },
          "name": "{{$node}}",
          "hostname": "{{$node}}",
          "logRotate": {
              "sizeThresholdMB": 1000,
              "timeThresholdHrs": 24
          },
          "version": "{{$.Version}}",
          "authSchemaVersion": 5,
          "processType": "mongos",
          "cluster": "{{$.ID}}_cluster"
      },{{end}}

      {{range $i, $node := .Cluster.ConfigServers}}{
          "args2_6": {
              "net": {
                  "port": 28000
              },
              "replication": {
                  "replSetName": "{{$.ID}}_config"
              },
              "sharding": {
                "clusterRole": "configsvr"
              },
              "storage": {
                  "dbPath": "/var/vcap/store/mongodb-data"
              },
              "systemLog": {
                  "destination": "file",
                  "path": "/var/vcap/sys/log/mongod_node/mongodb.log"
              }
          },
          "name": "{{$node}}",
          "hostname": "{{$node}}",
          "logRotate": {
              "sizeThresholdMB": 1000,
              "timeThresholdHrs": 24
          },
          "version": "{{$.Version}}",
          "authSchemaVersion": 5,
          "processType": "mongod"
      }{{if last $.Cluster.ConfigServers $i}}{{else}},{{end}}{{end}}

      {{range $ii, $shard := .Cluster.Shards}}
          {{range $i, $node := $shard}},{
              "args2_6": {
                  "net": {
                      "port": 28000
                  },
                  "replication": {
                      "replSetName": "{{$.ID}}_shard_{{$ii}}"
                  },
                  "storage": {
                       "dbPath": "/var/vcap/store/mongodb-data"
                  },
                  "systemLog": {
                      "destination": "file",
                      "path": "/var/vcap/sys/log/mongod_node/mongodb.log"
                  }
              },
              "name": "{{$node}}",
              "hostname": "{{$node}}",
              "logRotate": {
                  "sizeThresholdMB": 1000,
                  "timeThresholdHrs": 24
              },
              "version": "{{$.Version}}",
              "authSchemaVersion": 5,
              "processType": "mongod"
          }{{end}}
      {{end}}
    ],

    "replicaSets": [{
        "_id": "{{$.ID}}_config",
        "members": [
            {{range $i, $node := .Cluster.ConfigServers}}{{if $i}},{{end}}{
                "_id": {{$i}},
                "arbiterOnly": false,
                "hidden": false,
                "host": "{{$node}}",
                "priority": 1,
                "slaveDelay": 0,
                "votes": 1
            }{{end}}
        ]
    }
    {{range $i, $shard := .Cluster.Shards}},{
        "_id": "{{$.ID}}_shard_{{$i}}",
        "members": [{{range $i, $node := $shard}}
            {{if $i}},{{end}}{
                "_id": {{$i}},
                "arbiterOnly": false,
                "hidden": false,
                "host": "{{$node}}",
                "priority": 1,
                "slaveDelay": 0,
                "votes": 1
            }
            {{end}}
        ]
    }{{end}}],

    "sharding": [{
        "shards": [
             {{range $i, $shard := .Cluster.Shards}}{{if $i}},{{end}}{
                 "tags": [],
                 "_id": "{{$.ID}}_shard_{{$i}}",
                 "rs": "{{$.ID}}_shard_{{$i}}"
             }{{end}}
        ],
        "name": "{{.ID}}_cluster",
        "configServer": [],
        "configServerReplica": "{{.ID}}_config",
        "collections": []
    }],

    "auth": {
        "autoUser": "mms-automation",
        "autoPwd": "{{.Password}}",
        "deploymentAuthMechanisms": [
            "SCRAM-SHA-1"
        ],
        "key": "{{.Key}}",
        "keyfile": "/var/vcap/jobs/mongod_node/config/mongo_om.key",
        "disabled": false,
        "usersDeleted": [],
        "usersWanted": [
            {
                "db": "admin",
                "roles": [
                    {
                        "db": "admin",
                        "role": "clusterMonitor"
                    }
                ],
                "user": "mms-monitoring-agent",
                "initPwd": "{{.Password}}"
            },
            {
                "db": "admin",
                "roles": [
                    {
                        "db": "admin",
                        "role": "clusterAdmin"
                    },
                    {
                        "db": "admin",
                        "role": "readAnyDatabase"
                    },
                    {
                        "db": "admin",
                        "role": "userAdminAnyDatabase"
                    },
                    {
                        "db": "local",
                        "role": "readWrite"
                    },
                    {
                        "db": "admin",
                        "role": "readWrite"
                    }
                ],
                "user": "mms-backup-agent",
                "initPwd": "{{.Password}}"
            },
            {
               "db": "admin" ,
               "user": "admin" ,
               "roles": [
                 {
                     "db": "admin",
                     "role": "clusterAdmin"
                 },
                 {
                     "db": "admin",
                     "role": "readAnyDatabase"
                 },
                 {
                     "db": "admin",
                     "role": "userAdminAnyDatabase"
                 },
                 {
                     "db": "local",
                     "role": "readWrite"
                 },
                 {
                     "db": "admin",
                     "role": "readWrite"
                 }
               ],
               "initPwd": "{{.AdminPassword}}"
            }
        ],
        "autoAuthMechanism": "SCRAM-SHA-1"
    }
}`,

	PlanReplicaSet: `{
    "options": {
        "downloadBase": "/var/lib/mongodb-mms-automation",
    },
    "mongoDbVersions": [
        {"name": "{{.Version}}"}
    ],
    "backupVersions": [{
        "hostname": "{{index .Nodes 0}}",
        "logPath": "/var/vcap/sys/log/mongod_node/backup-agent.log",
        "logRotate": {
            "sizeThresholdMB": 1000,
            "timeThresholdHrs": 24
        }
    }],
    "monitoringVersions": [{
        "hostname": "{{index .Nodes 0}}",
        "logPath": "/var/vcap/sys/log/mongod_node/monitoring-agent.log",
        "logRotate": {
            "sizeThresholdMB": 1000,
            "timeThresholdHrs": 24
        }
    }],
    "processes": [{{range $i, $node := .Nodes}}
      {{if $i}},{{end}}{
        "args2_6": {
            "net": {
                "port": 28000
            },
            "replication": {
                "replSetName": "pcf_repl"
            },
            "storage": {
                "dbPath": "/var/vcap/store/mongodb-data"
            },
            "systemLog": {
                "destination": "file",
                "path": "/var/vcap/sys/log/mongod_node/mongodb.log"
            }
        },
        "hostname": "{{$node}}",
        "logRotate": {
            "sizeThresholdMB": 1000,
            "timeThresholdHrs": 24
        },
        "name": "{{$node}}",
        "processType": "mongod",
        "version": "{{$.Version}}",
        "authSchemaVersion": 5
    }
    {{end}}
  ],
    "replicaSets": [{
        "_id": "pcf_repl",
        "members": [
          {{range $i, $node := .Nodes}}
          {{if $i}},{{end}}{
            "_id": {{$i}},
            "host": "{{$node}}"{{if last $.Nodes $i}},
            "arbiterOnly": true,
            "priority": 0
            {{end}}
          }
          {{end}}
        ]
    }],
    "roles": [],
    "sharding": [],

    "auth": {
        "autoUser": "mms-automation",
        "autoPwd": "{{.Password}}",
        "deploymentAuthMechanisms": [
            "SCRAM-SHA-1"
        ],
        "key": "{{.Key}}",
        "keyfile": "/var/vcap/jobs/mongod_node/config/mongo_om.key",
        "disabled": false,
        "usersDeleted": [],
        "usersWanted": [
            {
                "db": "admin",
                "roles": [
                    {
                        "db": "admin",
                        "role": "clusterMonitor"
                    }
                ],
                "user": "mms-monitoring-agent",
                "initPwd": "{{.Password}}"
            },
            {
                "db": "admin",
                "roles": [
                    {
                        "db": "admin",
                        "role": "clusterAdmin"
                    },
                    {
                        "db": "admin",
                        "role": "readAnyDatabase"
                    },
                    {
                        "db": "admin",
                        "role": "userAdminAnyDatabase"
                    },
                    {
                        "db": "local",
                        "role": "readWrite"
                    },
                    {
                        "db": "admin",
                        "role": "readWrite"
                    }
                ],
                "user": "mms-backup-agent",
                "initPwd": "{{.Password}}"
            },
            {
               "db": "admin" ,
               "user": "admin" ,
               "roles": [
                 {
                     "db": "admin",
                     "role": "clusterAdmin"
                 },
                 {
                     "db": "admin",
                     "role": "readAnyDatabase"
                 },
                 {
                     "db": "admin",
                     "role": "userAdminAnyDatabase"
                 },
                 {
                     "db": "local",
                     "role": "readWrite"
                 },
                 {
                     "db": "admin",
                     "role": "readWrite"
                 }
               ],
               "initPwd": "{{.AdminPassword}}"
            }
        ],
        "autoAuthMechanism": "SCRAM-SHA-1"
    }
}`,
}
<commit_msg>Install monitoring agent on each node<commit_after>package adapter

import (
	"reflect"
	"text/template"
)

const (
	PlanStandalone     = "standalone"
	PlanReplicaSet     = "replica_set"
	PlanShardedCluster = "sharded_cluster"
)

var plans = map[string]*template.Template{}

func init() {
	funcs := template.FuncMap{
		"last": func(a interface{}, x int) bool {
			return reflect.ValueOf(a).Len()-1 == x
		},
	}

	var err error
	for k, s := range plansRaw {
		plans[k], err = template.New(string(k)).Funcs(funcs).Parse(s)
		if err != nil {
			panic(err)
		}
	}
}

var plansRaw = map[string]string{
	PlanStandalone: `{
    "options": {
        "downloadBase": "/var/lib/mongodb-mms-automation",
    },
    "mongoDbVersions": [
        {"name": "{{.Version}}"}
    ],
    "backupVersions": [{
        "hostname": "{{index .Nodes 0}}",
        "logPath": "/var/vcap/sys/log/mongod_node/backup-agent.log",
        "logRotate": {
            "sizeThresholdMB": 1000,
            "timeThresholdHrs": 24
        }
    }],
    "monitoringVersions": [
        {{range $i, $node := .Nodes}}{{if $i}},{{end}}{
            "hostname": "$node}}",
            "logPath": "/var/vcap/sys/log/mongod_node/monitoring-agent.log",
            "logRotate": {
                "sizeThresholdMB": 1000,
                "timeThresholdHrs": 24
            }
        }{{end}}
    ],
    "processes": [{
        "args2_6": {
            "net": {
                "port": 28000
            },
            "storage": {
                "dbPath": "/var/vcap/store/mongodb-data"
            },
            "systemLog": {
                "destination": "file",
                "path": "/var/vcap/sys/log/mongod_node/mongodb.log"
            }
        },
        "hostname": "{{index .Nodes 0}}",
        "logRotate": {
            "sizeThresholdMB": 1000,
            "timeThresholdHrs": 24
        },
        "name": "{{index .Nodes 0}}",
        "processType": "mongod",
        "version": "{{.Version}}",
        "authSchemaVersion": 5
    }],
    "replicaSets": [],
    "roles": [],
    "sharding": [],

    "auth": {
        "autoUser": "mms-automation",
        "autoPwd": "{{.Password}}",
        "deploymentAuthMechanisms": [
            "SCRAM-SHA-1"
        ],
        "key": "{{.Key}}",
        "keyfile": "/var/vcap/jobs/mongod_node/config/mongo_om.key",
        "disabled": false,
        "usersDeleted": [],
        "usersWanted": [
            {
                "db": "admin",
                "roles": [
                    {
                        "db": "admin",
                        "role": "clusterMonitor"
                    }
                ],
                "user": "mms-monitoring-agent",
                "initPwd": "{{.Password}}"
            },
            {
                "db": "admin",
                "roles": [
                    {
                        "db": "admin",
                        "role": "clusterAdmin"
                    },
                    {
                        "db": "admin",
                        "role": "readAnyDatabase"
                    },
                    {
                        "db": "admin",
                        "role": "userAdminAnyDatabase"
                    },
                    {
                        "db": "local",
                        "role": "readWrite"
                    },
                    {
                        "db": "admin",
                        "role": "readWrite"
                    }
                ],
                "user": "mms-backup-agent",
                "initPwd": "{{.Password}}"
            },
            {
               "db": "admin" ,
               "user": "admin" ,
               "roles": [
                 {
                     "db": "admin",
                     "role": "clusterAdmin"
                 },
                 {
                     "db": "admin",
                     "role": "readAnyDatabase"
                 },
                 {
                     "db": "admin",
                     "role": "userAdminAnyDatabase"
                 },
                 {
                     "db": "local",
                     "role": "readWrite"
                 },
                 {
                     "db": "admin",
                     "role": "readWrite"
                 }
               ],
               "initPwd": "{{.AdminPassword}}"
            }
        ],
        "autoAuthMechanism": "SCRAM-SHA-1"
    }
}`,

	PlanShardedCluster: `{
    "options": {
        "downloadBase": "/var/lib/mongodb-mms-automation",
    },
    "mongoDbVersions": [
        {"name": "{{.Version}}"}
    ],
    "backupVersions": [
    ],
    "monitoringVersions": [{
        {{range $i, $node := .Nodes}}{{if $i}},{{end}}{
            "hostname": "{{$node}}",
            "logPath": "/var/vcap/sys/log/mongod_node/monitoring-agent.log",
            "logRotate": {
                "sizeThresholdMB": 1000,
                "timeThresholdHrs": 24
            }
        }{{end}}
    }],
    "processes": [
      {{range $i, $node := .Cluster.Routers}}{
          "args2_6": {
              "net": {
                  "port": 28000
              },
              "systemLog": {
                  "destination": "file",
                  "path": "/var/vcap/sys/log/mongod_node/mongodb.log"
              }
          },
          "name": "{{$node}}",
          "hostname": "{{$node}}",
          "logRotate": {
              "sizeThresholdMB": 1000,
              "timeThresholdHrs": 24
          },
          "version": "{{$.Version}}",
          "authSchemaVersion": 5,
          "processType": "mongos",
          "cluster": "{{$.ID}}_cluster"
      },{{end}}

      {{range $i, $node := .Cluster.ConfigServers}}{
          "args2_6": {
              "net": {
                  "port": 28000
              },
              "replication": {
                  "replSetName": "{{$.ID}}_config"
              },
              "sharding": {
                "clusterRole": "configsvr"
              },
              "storage": {
                  "dbPath": "/var/vcap/store/mongodb-data"
              },
              "systemLog": {
                  "destination": "file",
                  "path": "/var/vcap/sys/log/mongod_node/mongodb.log"
              }
          },
          "name": "{{$node}}",
          "hostname": "{{$node}}",
          "logRotate": {
              "sizeThresholdMB": 1000,
              "timeThresholdHrs": 24
          },
          "version": "{{$.Version}}",
          "authSchemaVersion": 5,
          "processType": "mongod"
      }{{if last $.Cluster.ConfigServers $i}}{{else}},{{end}}{{end}}

      {{range $ii, $shard := .Cluster.Shards}}
          {{range $i, $node := $shard}},{
              "args2_6": {
                  "net": {
                      "port": 28000
                  },
                  "replication": {
                      "replSetName": "{{$.ID}}_shard_{{$ii}}"
                  },
                  "storage": {
                       "dbPath": "/var/vcap/store/mongodb-data"
                  },
                  "systemLog": {
                      "destination": "file",
                      "path": "/var/vcap/sys/log/mongod_node/mongodb.log"
                  }
              },
              "name": "{{$node}}",
              "hostname": "{{$node}}",
              "logRotate": {
                  "sizeThresholdMB": 1000,
                  "timeThresholdHrs": 24
              },
              "version": "{{$.Version}}",
              "authSchemaVersion": 5,
              "processType": "mongod"
          }{{end}}
      {{end}}
    ],

    "replicaSets": [{
        "_id": "{{$.ID}}_config",
        "members": [
            {{range $i, $node := .Cluster.ConfigServers}}{{if $i}},{{end}}{
                "_id": {{$i}},
                "arbiterOnly": false,
                "hidden": false,
                "host": "{{$node}}",
                "priority": 1,
                "slaveDelay": 0,
                "votes": 1
            }{{end}}
        ]
    }
    {{range $i, $shard := .Cluster.Shards}},{
        "_id": "{{$.ID}}_shard_{{$i}}",
        "members": [{{range $i, $node := $shard}}
            {{if $i}},{{end}}{
                "_id": {{$i}},
                "arbiterOnly": false,
                "hidden": false,
                "host": "{{$node}}",
                "priority": 1,
                "slaveDelay": 0,
                "votes": 1
            }
            {{end}}
        ]
    }{{end}}],

    "sharding": [{
        "shards": [
             {{range $i, $shard := .Cluster.Shards}}{{if $i}},{{end}}{
                 "tags": [],
                 "_id": "{{$.ID}}_shard_{{$i}}",
                 "rs": "{{$.ID}}_shard_{{$i}}"
             }{{end}}
        ],
        "name": "{{.ID}}_cluster",
        "configServer": [],
        "configServerReplica": "{{.ID}}_config",
        "collections": []
    }],

    "auth": {
        "autoUser": "mms-automation",
        "autoPwd": "{{.Password}}",
        "deploymentAuthMechanisms": [
            "SCRAM-SHA-1"
        ],
        "key": "{{.Key}}",
        "keyfile": "/var/vcap/jobs/mongod_node/config/mongo_om.key",
        "disabled": false,
        "usersDeleted": [],
        "usersWanted": [
            {
                "db": "admin",
                "roles": [
                    {
                        "db": "admin",
                        "role": "clusterMonitor"
                    }
                ],
                "user": "mms-monitoring-agent",
                "initPwd": "{{.Password}}"
            },
            {
                "db": "admin",
                "roles": [
                    {
                        "db": "admin",
                        "role": "clusterAdmin"
                    },
                    {
                        "db": "admin",
                        "role": "readAnyDatabase"
                    },
                    {
                        "db": "admin",
                        "role": "userAdminAnyDatabase"
                    },
                    {
                        "db": "local",
                        "role": "readWrite"
                    },
                    {
                        "db": "admin",
                        "role": "readWrite"
                    }
                ],
                "user": "mms-backup-agent",
                "initPwd": "{{.Password}}"
            },
            {
               "db": "admin" ,
               "user": "admin" ,
               "roles": [
                 {
                     "db": "admin",
                     "role": "clusterAdmin"
                 },
                 {
                     "db": "admin",
                     "role": "readAnyDatabase"
                 },
                 {
                     "db": "admin",
                     "role": "userAdminAnyDatabase"
                 },
                 {
                     "db": "local",
                     "role": "readWrite"
                 },
                 {
                     "db": "admin",
                     "role": "readWrite"
                 }
               ],
               "initPwd": "{{.AdminPassword}}"
            }
        ],
        "autoAuthMechanism": "SCRAM-SHA-1"
    }
}`,

	PlanReplicaSet: `{
    "options": {
        "downloadBase": "/var/lib/mongodb-mms-automation",
    },
    "mongoDbVersions": [
        {"name": "{{.Version}}"}
    ],
    "backupVersions": [{
        "hostname": "{{index .Nodes 0}}",
        "logPath": "/var/vcap/sys/log/mongod_node/backup-agent.log",
        "logRotate": {
            "sizeThresholdMB": 1000,
            "timeThresholdHrs": 24
        }
    }],
    "monitoringVersions": [
        {{range $i, $node := .Nodes}}{{if $i}},{{end}}{
            "hostname": "{{$node}}",
            "logPath": "/var/vcap/sys/log/mongod_node/monitoring-agent.log",
            "logRotate": {
                "sizeThresholdMB": 1000,
                "timeThresholdHrs": 24
            }
        }{{end}}
    ],
    "processes": [{{range $i, $node := .Nodes}}
      {{if $i}},{{end}}{
        "args2_6": {
            "net": {
                "port": 28000
            },
            "replication": {
                "replSetName": "pcf_repl"
            },
            "storage": {
                "dbPath": "/var/vcap/store/mongodb-data"
            },
            "systemLog": {
                "destination": "file",
                "path": "/var/vcap/sys/log/mongod_node/mongodb.log"
            }
        },
        "hostname": "{{$node}}",
        "logRotate": {
            "sizeThresholdMB": 1000,
            "timeThresholdHrs": 24
        },
        "name": "{{$node}}",
        "processType": "mongod",
        "version": "{{$.Version}}",
        "authSchemaVersion": 5
    }
    {{end}}
  ],
    "replicaSets": [{
        "_id": "pcf_repl",
        "members": [
          {{range $i, $node := .Nodes}}
          {{if $i}},{{end}}{
            "_id": {{$i}},
            "host": "{{$node}}"{{if last $.Nodes $i}},
            "arbiterOnly": true,
            "priority": 0
            {{end}}
          }
          {{end}}
        ]
    }],
    "roles": [],
    "sharding": [],

    "auth": {
        "autoUser": "mms-automation",
        "autoPwd": "{{.Password}}",
        "deploymentAuthMechanisms": [
            "SCRAM-SHA-1"
        ],
        "key": "{{.Key}}",
        "keyfile": "/var/vcap/jobs/mongod_node/config/mongo_om.key",
        "disabled": false,
        "usersDeleted": [],
        "usersWanted": [
            {
                "db": "admin",
                "roles": [
                    {
                        "db": "admin",
                        "role": "clusterMonitor"
                    }
                ],
                "user": "mms-monitoring-agent",
                "initPwd": "{{.Password}}"
            },
            {
                "db": "admin",
                "roles": [
                    {
                        "db": "admin",
                        "role": "clusterAdmin"
                    },
                    {
                        "db": "admin",
                        "role": "readAnyDatabase"
                    },
                    {
                        "db": "admin",
                        "role": "userAdminAnyDatabase"
                    },
                    {
                        "db": "local",
                        "role": "readWrite"
                    },
                    {
                        "db": "admin",
                        "role": "readWrite"
                    }
                ],
                "user": "mms-backup-agent",
                "initPwd": "{{.Password}}"
            },
            {
               "db": "admin" ,
               "user": "admin" ,
               "roles": [
                 {
                     "db": "admin",
                     "role": "clusterAdmin"
                 },
                 {
                     "db": "admin",
                     "role": "readAnyDatabase"
                 },
                 {
                     "db": "admin",
                     "role": "userAdminAnyDatabase"
                 },
                 {
                     "db": "local",
                     "role": "readWrite"
                 },
                 {
                     "db": "admin",
                     "role": "readWrite"
                 }
               ],
               "initPwd": "{{.AdminPassword}}"
            }
        ],
        "autoAuthMechanism": "SCRAM-SHA-1"
    }
}`,
}
<|endoftext|>


<commit_before>package atomicgoleveldb

import "fmt"
import "hash/fnv"

import "github.com/syndtr/goleveldb/leveldb"
import "github.com/syndtr/goleveldb/leveldb/opt"

const DEFAULT_NUM_BUCKETS = 16
const DEFAULT_BUFFER_SIZE = 8

type Config struct {
	nBuckets uint
	bufferSz uint
}

func NewConfig() *Config {
	return new(Config)
}

func (c *Config) NumBuckets() uint {
	if c.nBuckets != 0 {
		return c.nBuckets
	} else {
		return DEFAULT_NUM_BUCKETS
	}
}

func (c *Config) BufferSize() uint {
	if c.bufferSz != 0 {
		return c.bufferSz
	} else {
		return DEFAULT_BUFFER_SIZE
	}
}

func (c *Config) WithBuckets(n uint) {
	c.nBuckets = n
}

func (c *Config) WithBuffer(n uint) {
	c.bufferSz = n
}

type req struct {
	do func(*leveldb.DB) (interface{}, error)
	r  chan doRet
}

type doRet struct {
	i interface{}
	e error
}

type DB struct {
	db       *leveldb.DB
	requests []chan req
	stop     chan bool
	closed   bool
}

func Wrap(db *leveldb.DB, config *Config) *DB {
	ret := &DB{
		db:       db,
		requests: make([]chan req, 0, config.NumBuckets()),
		stop:     make(chan bool, 1),
	}

	for len(ret.requests) < cap(ret.requests) {
		ret.requests = append(ret.requests, make(chan req, config.BufferSize()))
	}

	for i, _ := range ret.requests {
		go func(idx int) {
			reqs := ret.requests[idx]
			for {
				req := <-reqs
				result, err := req.do(ret.db)
				req.r <- doRet{
					i: result,
					e: err,
				}
			}
		}(i)
	}

	go func() {
		_ = <-ret.stop
		for i, _ := range ret.requests {
			close(ret.requests[i])
		}
	}()

	return ret
}

// TODO: Make sure this is threadsafe
func (db *DB) Close() error {
	db.closed = true
	defer func() {
		db.stop <- true
	}()
	return db.db.Close()
}

func (db *DB) getRange(key []byte) int {
	hasher := fnv.New64()
	hasher.Write(key)
	return int(hasher.Sum64()) % len(db.requests)
}

func (db *DB) Get(key []byte, ro *opt.ReadOptions) (value []byte, err error) {
	if db.closed {
		return nil, fmt.Errorf("Get(): db is already closed")
	}
	idx := db.getRange(key)
	result := make(chan doRet, 1)
	db.requests[idx] <- req{
		r: result,
		do: func(db *leveldb.DB) (interface{}, error) {
			val, er := db.Get(key, ro)
			return interface{}(val), er
		},
	}

	ret := <-result
	value = ret.i.([]byte)
	err = ret.e
	return
}

type Tx struct {
	key string
}
<commit_msg>Most of the trivial wrappers finished<commit_after>package atomicgoleveldb

import "fmt"
import "hash/fnv"

import "github.com/syndtr/goleveldb/leveldb"
import "github.com/syndtr/goleveldb/leveldb/opt"
import "github.com/syndtr/goleveldb/leveldb/util"

const DEFAULT_NUM_BUCKETS = 16
const DEFAULT_BUFFER_SIZE = 8

type Config struct {
	nBuckets uint
	bufferSz uint
}

func NewConfig() *Config {
	return new(Config)
}

func (c *Config) NumBuckets() uint {
	if c.nBuckets != 0 {
		return c.nBuckets
	} else {
		return DEFAULT_NUM_BUCKETS
	}
}

func (c *Config) BufferSize() uint {
	if c.bufferSz != 0 {
		return c.bufferSz
	} else {
		return DEFAULT_BUFFER_SIZE
	}
}

func (c *Config) WithBuckets(n uint) {
	c.nBuckets = n
}

func (c *Config) WithBuffer(n uint) {
	c.bufferSz = n
}

type req struct {
	do func(*leveldb.DB) (interface{}, error)
	r  chan doRet
}

type doRet struct {
	i interface{}
	e error
}

type DB struct {
	db       *leveldb.DB
	requests []chan req
	stop     chan bool
	closed   bool
}

func Wrap(db *leveldb.DB, config *Config) *DB {
	ret := &DB{
		db:       db,
		requests: make([]chan req, 0, config.NumBuckets()),
		stop:     make(chan bool, 1),
	}

	for len(ret.requests) < cap(ret.requests) {
		ret.requests = append(ret.requests, make(chan req, config.BufferSize()))
	}

	for i, _ := range ret.requests {
		go func(idx int) {
			reqs := ret.requests[idx]
			for {
				req := <-reqs
				result, err := req.do(ret.db)
				req.r <- doRet{
					i: result,
					e: err,
				}
			}
		}(i)
	}

	go func() {
		_ = <-ret.stop
		for i, _ := range ret.requests {
			close(ret.requests[i])
		}
	}()

	return ret
}

func (db *DB) getRange(key []byte) int {
	hasher := fnv.New64()
	hasher.Write(key)
	return int(hasher.Sum64()) % len(db.requests)
}

// TODO: Make sure this is threadsafe
func (db *DB) Close() error {
	db.closed = true
	defer func() {
		db.stop <- true
	}()
	return db.db.Close()
}

func (db *DB) wrapOperation(key []byte, f func(db *leveldb.DB) (interface{}, error)) doRet {
	idx := db.getRange(key)
	result := make(chan doRet, 1)
	db.requests[idx] <- req{
		r:  result,
		do: f,
	}
	ret := <-result
	return ret
}

func (db *DB) Get(key []byte, ro *opt.ReadOptions) (value []byte, err error) {
	if db.closed {
		return nil, fmt.Errorf("Get(): db is already closed")
	}
	ret := db.wrapOperation(key, func(db *leveldb.DB) (interface{}, error) {
		val, er := db.Get(key, ro)
		return interface{}(val), er
	})

	value = ret.i.([]byte)
	err = ret.e
	return
}

func (db *DB) Delete(key []byte, wo *opt.WriteOptions) error {
	if db.closed {
		return fmt.Errorf("Get(): db is already closed")
	}
	ret := db.wrapOperation(key, func(db *leveldb.DB) (interface{}, error) {
		er := db.Delete(key, wo)
		return nil, er
	})

	return ret.e
}

func (db *DB) Has(key []byte, ro *opt.ReadOptions) (ret bool, err error) {
	if db.closed {
		return false, fmt.Errorf("Has(): db is already closed")
	}
	r := db.wrapOperation(key, func(db *leveldb.DB) (interface{}, error) {
		bl, er := db.Has(key, ro)
		return interface{}(bl), er
	})

	ret = r.i.(bool)
	err = r.e
	return
}

func (db *DB) Put(key, value []byte, wo *opt.WriteOptions) error {
	if db.closed {
		return fmt.Errorf("Has(): db is already closed")
	}
	ret := db.wrapOperation(key, func(db *leveldb.DB) (interface{}, error) {
		er := db.Put(key, value, wo)
		return nil, er
	})

	return ret.e
}

type Tx struct {
	key string
	db  *DB
}

func (db *DB) AtomicallyDo(key []byte, f func(tx *Tx) (interface{}, error)) (interface{}, error) {
	panic("unimplemented!")
}

// Wrappers for all the functions at a normal DB has (and that we can guarantee
// to be safe to run)
func (db *DB) CompactRange(r util.Range) error {
	return db.db.CompactRange(r)
}

func (db *DB) GetProperty(name string) (string, error) {
	return db.GetProperty(name)
}

func (db *DB) GetSnapshot() (*leveldb.Snapshot, error) {
	return db.GetSnapshot()
}

func (db *DB) SetReadOnly() error {
	return db.SetReadOnly()
}

func (db *DB) SizeOf(ranges []util.Range) (leveldb.Sizes, error) {
	return db.SizeOf(ranges)
}
<|endoftext|>