<commit_before>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package local_test

import (
	"os"
	"path/filepath"
	"runtime"
	"syscall"

	gc "launchpad.net/gocheck"

	"launchpad.net/juju-core/environs/config"
	"launchpad.net/juju-core/juju/osenv"
	"launchpad.net/juju-core/provider"
	"launchpad.net/juju-core/provider/local"
	"launchpad.net/juju-core/testing"
)

type configSuite struct {
	baseProviderSuite
	oldUser string
}

var _ = gc.Suite(&configSuite{})

func (s *configSuite) SetUpTest(c *gc.C) {
	s.baseProviderSuite.SetUpTest(c)
	s.oldUser = os.Getenv("USER")
	err := os.Setenv("USER", "tester")
	c.Assert(err, gc.IsNil)
}

func (s *configSuite) TearDownTest(c *gc.C) {
	os.Setenv("USER", s.oldUser)
	s.baseProviderSuite.TearDownTest(c)
}

func minimalConfigValues() map[string]interface{} {
	return testing.FakeConfig().Merge(testing.Attrs{
		"name": "test",
		"type": provider.Local,
	})
}

func minimalConfig(c *gc.C) *config.Config {
	minimal := minimalConfigValues()
	testConfig, err := config.New(config.NoDefaults, minimal)
	c.Assert(err, gc.IsNil)
	return testConfig
}

func (s *configSuite) TestValidateConfig(c *gc.C) {
	testConfig := minimalConfig(c)

	valid, err := local.Provider.Validate(testConfig, nil)
	c.Assert(err, gc.IsNil)

	expectedRootDir := filepath.Join(osenv.Home(), ".juju", "test")
	unknownAttrs := valid.UnknownAttrs()
	c.Assert(unknownAttrs["root-dir"], gc.Equals, expectedRootDir)
}

func (s *configSuite) TestValidateConfigWithRootDir(c *gc.C) {
	values := minimalConfigValues()
	root := c.MkDir()
	values["root-dir"] = root
	testConfig, err := config.New(config.NoDefaults, values)
	c.Assert(err, gc.IsNil)

	valid, err := local.Provider.Validate(testConfig, nil)
	c.Assert(err, gc.IsNil)
	unknownAttrs := valid.UnknownAttrs()
	c.Assert(unknownAttrs["root-dir"], gc.Equals, root)
}

func (s *configSuite) TestValidateConfigWithTildeInRootDir(c *gc.C) {
	values := minimalConfigValues()
	values["root-dir"] = "~/.juju/foo"
	testConfig, err := config.New(config.NoDefaults, values)
	c.Assert(err, gc.IsNil)

	valid, err := local.Provider.Validate(testConfig, nil)
	c.Assert(err, gc.IsNil)

	expectedRootDir := filepath.Join(osenv.Home(), ".juju", "foo")
	unknownAttrs := valid.UnknownAttrs()
	c.Assert(unknownAttrs["root-dir"], gc.Equals, expectedRootDir)
}

func (s *configSuite) TestNamespace(c *gc.C) {
	testConfig := minimalConfig(c)
	c.Assert(local.ConfigNamespace(testConfig), gc.Equals, "tester-test")
}

func (s *configSuite) TestNamespaceRootNoSudo(c *gc.C) {
	restore := local.SetRootCheckFunction(func() bool { return true })
	defer restore()
	err := os.Setenv("USER", "root")
	c.Assert(err, gc.IsNil)
	testConfig := minimalConfig(c)
	c.Assert(local.ConfigNamespace(testConfig), gc.Equals, "root-test")
}

func (s *configSuite) TestNamespaceRootWithSudo(c *gc.C) {
	restore := local.SetRootCheckFunction(func() bool { return true })
	defer restore()
	err := os.Setenv("USER", "root")
	c.Assert(err, gc.IsNil)
	err = os.Setenv("SUDO_USER", "tester")
	c.Assert(err, gc.IsNil)
	defer os.Setenv("SUDO_USER", "")
	testConfig := minimalConfig(c)
	c.Assert(local.ConfigNamespace(testConfig), gc.Equals, "tester-test")
}

func (s *configSuite) TestSudoCallerIds(c *gc.C) {
	defer os.Setenv("SUDO_UID", os.Getenv("SUDO_UID"))
	defer os.Setenv("SUDO_GID", os.Getenv("SUDO_GID"))
	for _, test := range []struct {
		uid         string
		gid         string
		errString   string
		expectedUid int
		expectedGid int
	}{{
		uid: "",
		gid: "",
	}, {
		uid:         "1001",
		gid:         "1002",
		expectedUid: 1001,
		expectedGid: 1002,
	}, {
		uid:       "1001",
		gid:       "foo",
		errString: `invalid value "foo" for SUDO_GID`,
	}, {
		uid:       "foo",
		gid:       "bar",
		errString: `invalid value "foo" for SUDO_UID`,
	}} {
		os.Setenv("SUDO_UID", test.uid)
		os.Setenv("SUDO_GID", test.gid)
		uid, gid, err := local.SudoCallerIds()
		if test.errString == "" {
			c.Assert(err, gc.IsNil)
			c.Assert(uid, gc.Equals, test.expectedUid)
			c.Assert(gid, gc.Equals, test.expectedGid)
		} else {
			c.Assert(err, gc.ErrorMatches, test.errString)
			c.Assert(uid, gc.Equals, 0)
			c.Assert(gid, gc.Equals, 0)
		}
	}
}

type configRootSuite struct {
	baseProviderSuite
}

var _ = gc.Suite(&configRootSuite{})

func (s *configRootSuite) SetUpTest(c *gc.C) {
	s.baseProviderSuite.SetUpTest(c)
	// Skip if not linux
	if runtime.GOOS != "linux" {
		c.Skip("not running linux")
	}
	// Skip if not running as root.
	if os.Getuid() != 0 {
		c.Skip("not running as root")
	}
}

func (s *configRootSuite) TestCreateDirsNoUserJustRoot(c *gc.C) {
	defer os.Setenv("SUDO_UID", os.Getenv("SUDO_UID"))
	defer os.Setenv("SUDO_GID", os.Getenv("SUDO_GID"))

	os.Setenv("SUDO_UID", "")
	os.Setenv("SUDO_GID", "")

	testConfig := minimalConfig(c)
	err := local.CreateDirs(c, testConfig)
	c.Assert(err, gc.IsNil)
	// Check that the dirs are owned by root.
	for _, dir := range local.CheckDirs(c, testConfig) {
		info, err := os.Stat(dir)
		c.Assert(err, gc.IsNil)
		// This call is linux specific, but then so is sudo
		c.Assert(info.Sys().(*syscall.Stat_t).Uid, gc.Equals, uint32(0))
		c.Assert(info.Sys().(*syscall.Stat_t).Gid, gc.Equals, uint32(0))
	}
}

func (s *configRootSuite) TestCreateDirsAsUser(c *gc.C) {
	defer os.Setenv("SUDO_UID", os.Getenv("SUDO_UID"))
	defer os.Setenv("SUDO_GID", os.Getenv("SUDO_GID"))

	os.Setenv("SUDO_UID", "1000")
	os.Setenv("SUDO_GID", "1000")

	testConfig := minimalConfig(c)
	err := local.CreateDirs(c, testConfig)
	c.Assert(err, gc.IsNil)
	// Check that the dirs are owned by the UID/GID set above..
	for _, dir := range local.CheckDirs(c, testConfig) {
		info, err := os.Stat(dir)
		c.Assert(err, gc.IsNil)
		// This call is linux specific, but then so is sudo
		c.Assert(info.Sys().(*syscall.Stat_t).Uid, gc.Equals, uint32(1000))
		c.Assert(info.Sys().(*syscall.Stat_t).Gid, gc.Equals, uint32(1000))
	}
}
<commit_msg>Failing test.<commit_after>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package local_test

import (
	"os"
	"path/filepath"
	"runtime"
	"syscall"

	gc "launchpad.net/gocheck"

	"launchpad.net/juju-core/environs/config"
	"launchpad.net/juju-core/juju/osenv"
	"launchpad.net/juju-core/provider"
	"launchpad.net/juju-core/provider/local"
	"launchpad.net/juju-core/testing"
)

type configSuite struct {
	baseProviderSuite
	oldUser string
}

var _ = gc.Suite(&configSuite{})

func (s *configSuite) SetUpTest(c *gc.C) {
	s.baseProviderSuite.SetUpTest(c)
	s.oldUser = os.Getenv("USER")
	err := os.Setenv("USER", "tester")
	c.Assert(err, gc.IsNil)
}

func (s *configSuite) TearDownTest(c *gc.C) {
	os.Setenv("USER", s.oldUser)
	s.baseProviderSuite.TearDownTest(c)
}

func minimalConfigValues() map[string]interface{} {
	return testing.FakeConfig().Merge(testing.Attrs{
		"name": "test",
		"type": provider.Local,
	})
}

func minimalConfig(c *gc.C) *config.Config {
	minimal := minimalConfigValues()
	testConfig, err := config.New(config.NoDefaults, minimal)
	c.Assert(err, gc.IsNil)
	return testConfig
}

func (s *configSuite) TestValidateConfig(c *gc.C) {
	testConfig := minimalConfig(c)

	valid, err := local.Provider.Validate(testConfig, nil)
	c.Assert(err, gc.IsNil)

	expectedRootDir := filepath.Join(osenv.Home(), ".juju", "test")
	unknownAttrs := valid.UnknownAttrs()
	c.Assert(unknownAttrs["root-dir"], gc.Equals, expectedRootDir)
}

func (s *configSuite) TestValidateConfigWithRootDir(c *gc.C) {
	values := minimalConfigValues()
	root := c.MkDir()
	values["root-dir"] = root
	testConfig, err := config.New(config.NoDefaults, values)
	c.Assert(err, gc.IsNil)

	valid, err := local.Provider.Validate(testConfig, nil)
	c.Assert(err, gc.IsNil)
	unknownAttrs := valid.UnknownAttrs()
	c.Assert(unknownAttrs["root-dir"], gc.Equals, root)
}

func (s *configSuite) TestValidateConfigWithTildeInRootDir(c *gc.C) {
	values := minimalConfigValues()
	values["root-dir"] = "~/.juju/foo"
	testConfig, err := config.New(config.NoDefaults, values)
	c.Assert(err, gc.IsNil)

	valid, err := local.Provider.Validate(testConfig, nil)
	c.Assert(err, gc.IsNil)

	expectedRootDir := filepath.Join(osenv.Home(), ".juju", "foo")
	unknownAttrs := valid.UnknownAttrs()
	c.Assert(unknownAttrs["root-dir"], gc.Equals, expectedRootDir)
}

func (s *configSuite) TestValidateConfigWithFloatPort(c *gc.C) {
	values := minimalConfigValues()
	values["storage-port"] = float64(8040)
	testConfig, err := config.New(config.NoDefaults, values)
	c.Assert(err, gc.IsNil)

	valid, err := local.Provider.Validate(testConfig, nil)
	c.Assert(err, gc.IsNil)
	unknownAttrs := valid.UnknownAttrs()
	c.Assert(unknownAttrs["storage-port"], gc.Equals, int(8040))
}

func (s *configSuite) TestNamespace(c *gc.C) {
	testConfig := minimalConfig(c)
	c.Assert(local.ConfigNamespace(testConfig), gc.Equals, "tester-test")
}

func (s *configSuite) TestNamespaceRootNoSudo(c *gc.C) {
	restore := local.SetRootCheckFunction(func() bool { return true })
	defer restore()
	err := os.Setenv("USER", "root")
	c.Assert(err, gc.IsNil)
	testConfig := minimalConfig(c)
	c.Assert(local.ConfigNamespace(testConfig), gc.Equals, "root-test")
}

func (s *configSuite) TestNamespaceRootWithSudo(c *gc.C) {
	restore := local.SetRootCheckFunction(func() bool { return true })
	defer restore()
	err := os.Setenv("USER", "root")
	c.Assert(err, gc.IsNil)
	err = os.Setenv("SUDO_USER", "tester")
	c.Assert(err, gc.IsNil)
	defer os.Setenv("SUDO_USER", "")
	testConfig := minimalConfig(c)
	c.Assert(local.ConfigNamespace(testConfig), gc.Equals, "tester-test")
}

func (s *configSuite) TestSudoCallerIds(c *gc.C) {
	defer os.Setenv("SUDO_UID", os.Getenv("SUDO_UID"))
	defer os.Setenv("SUDO_GID", os.Getenv("SUDO_GID"))
	for _, test := range []struct {
		uid         string
		gid         string
		errString   string
		expectedUid int
		expectedGid int
	}{{
		uid: "",
		gid: "",
	}, {
		uid:         "1001",
		gid:         "1002",
		expectedUid: 1001,
		expectedGid: 1002,
	}, {
		uid:       "1001",
		gid:       "foo",
		errString: `invalid value "foo" for SUDO_GID`,
	}, {
		uid:       "foo",
		gid:       "bar",
		errString: `invalid value "foo" for SUDO_UID`,
	}} {
		os.Setenv("SUDO_UID", test.uid)
		os.Setenv("SUDO_GID", test.gid)
		uid, gid, err := local.SudoCallerIds()
		if test.errString == "" {
			c.Assert(err, gc.IsNil)
			c.Assert(uid, gc.Equals, test.expectedUid)
			c.Assert(gid, gc.Equals, test.expectedGid)
		} else {
			c.Assert(err, gc.ErrorMatches, test.errString)
			c.Assert(uid, gc.Equals, 0)
			c.Assert(gid, gc.Equals, 0)
		}
	}
}

type configRootSuite struct {
	baseProviderSuite
}

var _ = gc.Suite(&configRootSuite{})

func (s *configRootSuite) SetUpTest(c *gc.C) {
	s.baseProviderSuite.SetUpTest(c)
	// Skip if not linux
	if runtime.GOOS != "linux" {
		c.Skip("not running linux")
	}
	// Skip if not running as root.
	if os.Getuid() != 0 {
		c.Skip("not running as root")
	}
}

func (s *configRootSuite) TestCreateDirsNoUserJustRoot(c *gc.C) {
	defer os.Setenv("SUDO_UID", os.Getenv("SUDO_UID"))
	defer os.Setenv("SUDO_GID", os.Getenv("SUDO_GID"))

	os.Setenv("SUDO_UID", "")
	os.Setenv("SUDO_GID", "")

	testConfig := minimalConfig(c)
	err := local.CreateDirs(c, testConfig)
	c.Assert(err, gc.IsNil)
	// Check that the dirs are owned by root.
	for _, dir := range local.CheckDirs(c, testConfig) {
		info, err := os.Stat(dir)
		c.Assert(err, gc.IsNil)
		// This call is linux specific, but then so is sudo
		c.Assert(info.Sys().(*syscall.Stat_t).Uid, gc.Equals, uint32(0))
		c.Assert(info.Sys().(*syscall.Stat_t).Gid, gc.Equals, uint32(0))
	}
}

func (s *configRootSuite) TestCreateDirsAsUser(c *gc.C) {
	defer os.Setenv("SUDO_UID", os.Getenv("SUDO_UID"))
	defer os.Setenv("SUDO_GID", os.Getenv("SUDO_GID"))

	os.Setenv("SUDO_UID", "1000")
	os.Setenv("SUDO_GID", "1000")

	testConfig := minimalConfig(c)
	err := local.CreateDirs(c, testConfig)
	c.Assert(err, gc.IsNil)
	// Check that the dirs are owned by the UID/GID set above..
	for _, dir := range local.CheckDirs(c, testConfig) {
		info, err := os.Stat(dir)
		c.Assert(err, gc.IsNil)
		// This call is linux specific, but then so is sudo
		c.Assert(info.Sys().(*syscall.Stat_t).Uid, gc.Equals, uint32(1000))
		c.Assert(info.Sys().(*syscall.Stat_t).Gid, gc.Equals, uint32(1000))
	}
}
<|endoftext|>


<commit_before>package logrus_sentry

import (
	"encoding/json"
	"fmt"
	"runtime"
	"sync"
	"time"

	"github.com/getsentry/raven-go"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
)

var (
	severityMap = map[logrus.Level]raven.Severity{
		logrus.DebugLevel: raven.DEBUG,
		logrus.InfoLevel:  raven.INFO,
		logrus.WarnLevel:  raven.WARNING,
		logrus.ErrorLevel: raven.ERROR,
		logrus.FatalLevel: raven.FATAL,
		logrus.PanicLevel: raven.FATAL,
	}
)

// SentryHook delivers logs to a sentry server.
type SentryHook struct {
	// Timeout sets the time to wait for a delivery error from the sentry server.
	// If this is set to zero the server will not wait for any response and will
	// consider the message correctly sent.
	//
	// This is ignored for asynchronous hooks. If you want to set a timeout when
	// using an async hook (to bound the length of time that hook.Flush can take),
	// you probably want to create your own raven.Client and set
	// ravenClient.Transport.(*raven.HTTPTransport).Client.Timeout to set a
	// timeout on the underlying HTTP request instead.
	Timeout                 time.Duration
	StacktraceConfiguration StackTraceConfiguration

	client *raven.Client
	levels []logrus.Level

	serverName   string
	ignoreFields map[string]struct{}
	extraFilters map[string]func(interface{}) interface{}

	asynchronous bool

	mu sync.RWMutex
	wg sync.WaitGroup
}

// The Stacktracer interface allows an error type to return a raven.Stacktrace.
type Stacktracer interface {
	GetStacktrace() *raven.Stacktrace
}

type causer interface {
	Cause() error
}

type pkgErrorStackTracer interface {
	StackTrace() errors.StackTrace
}

// StackTraceConfiguration allows for configuring stacktraces
type StackTraceConfiguration struct {
	// whether stacktraces should be enabled
	Enable bool
	// the level at which to start capturing stacktraces
	Level logrus.Level
	// how many stack frames to skip before stacktrace starts recording
	Skip int
	// the number of lines to include around a stack frame for context
	Context int
	// the prefixes that will be matched against the stack frame.
	// if the stack frame's package matches one of these prefixes
	// sentry will identify the stack frame as "in_app"
	InAppPrefixes []string
	// whether sending exception type should be enabled.
	SendExceptionType bool
	// whether the exception type and message should be switched.
	SwitchExceptionTypeAndMessage bool
}

// NewSentryHook creates a hook to be added to an instance of logger
// and initializes the raven client.
// This method sets the timeout to 100 milliseconds.
func NewSentryHook(DSN string, levels []logrus.Level) (*SentryHook, error) {
	client, err := raven.New(DSN)
	if err != nil {
		return nil, err
	}
	return NewWithClientSentryHook(client, levels)
}

// NewWithTagsSentryHook creates a hook with tags to be added to an instance
// of logger and initializes the raven client. This method sets the timeout to
// 100 milliseconds.
func NewWithTagsSentryHook(DSN string, tags map[string]string, levels []logrus.Level) (*SentryHook, error) {
	client, err := raven.NewWithTags(DSN, tags)
	if err != nil {
		return nil, err
	}
	return NewWithClientSentryHook(client, levels)
}

// NewWithClientSentryHook creates a hook using an initialized raven client.
// This method sets the timeout to 100 milliseconds.
func NewWithClientSentryHook(client *raven.Client, levels []logrus.Level) (*SentryHook, error) {
	return &SentryHook{
		Timeout: 100 * time.Millisecond,
		StacktraceConfiguration: StackTraceConfiguration{
			Enable:            false,
			Level:             logrus.ErrorLevel,
			Skip:              5,
			Context:           0,
			InAppPrefixes:     nil,
			SendExceptionType: true,
		},
		client:       client,
		levels:       levels,
		ignoreFields: make(map[string]struct{}),
		extraFilters: make(map[string]func(interface{}) interface{}),
	}, nil
}

// NewAsyncSentryHook creates a hook same as NewSentryHook, but in asynchronous
// mode.
func NewAsyncSentryHook(DSN string, levels []logrus.Level) (*SentryHook, error) {
	hook, err := NewSentryHook(DSN, levels)
	return setAsync(hook), err
}

// NewAsyncWithTagsSentryHook creates a hook same as NewWithTagsSentryHook, but
// in asynchronous mode.
func NewAsyncWithTagsSentryHook(DSN string, tags map[string]string, levels []logrus.Level) (*SentryHook, error) {
	hook, err := NewWithTagsSentryHook(DSN, tags, levels)
	return setAsync(hook), err
}

// NewAsyncWithClientSentryHook creates a hook same as NewWithClientSentryHook,
// but in asynchronous mode.
func NewAsyncWithClientSentryHook(client *raven.Client, levels []logrus.Level) (*SentryHook, error) {
	hook, err := NewWithClientSentryHook(client, levels)
	return setAsync(hook), err
}

func setAsync(hook *SentryHook) *SentryHook {
	if hook == nil {
		return nil
	}
	hook.asynchronous = true
	return hook
}

// Fire is called when an event should be sent to sentry
// Special fields that sentry uses to give more information to the server
// are extracted from entry.Data (if they are found)
// These fields are: error, logger, server_name, http_request, tags
func (hook *SentryHook) Fire(entry *logrus.Entry) error {
	hook.mu.RLock() // Allow multiple go routines to log simultaneously
	defer hook.mu.RUnlock()
	packet := raven.NewPacket(entry.Message)
	packet.Timestamp = raven.Timestamp(entry.Time)
	packet.Level = severityMap[entry.Level]
	packet.Platform = "go"

	df := newDataField(entry.Data)

	// set special fields
	if hook.serverName != "" {
		packet.ServerName = hook.serverName
	}
	if logger, ok := df.getLogger(); ok {
		packet.Logger = logger
	}
	if serverName, ok := df.getServerName(); ok {
		packet.ServerName = serverName
	}
	if eventID, ok := df.getEventID(); ok {
		packet.EventID = eventID
	}
	if tags, ok := df.getTags(); ok {
		packet.Tags = tags
	}
	if fingerprint, ok := df.getFingerprint(); ok {
		packet.Fingerprint = fingerprint
	}
	if req, ok := df.getHTTPRequest(); ok {
		packet.Interfaces = append(packet.Interfaces, req)
	}
	if user, ok := df.getUser(); ok {
		packet.Interfaces = append(packet.Interfaces, user)
	}

	// set stacktrace data
	stConfig := &hook.StacktraceConfiguration
	if stConfig.Enable && entry.Level <= stConfig.Level {
		if err, ok := df.getError(); ok {
			var currentStacktrace *raven.Stacktrace
			currentStacktrace = hook.findStacktrace(err)
			if currentStacktrace == nil {
				currentStacktrace = raven.NewStacktrace(stConfig.Skip, stConfig.Context, stConfig.InAppPrefixes)
			}
			exc := raven.NewException(errors.Cause(err), currentStacktrace)
			if !stConfig.SendExceptionType {
				exc.Type = ""
			}
			if stConfig.SwitchExceptionTypeAndMessage {
				exc.Type, exc.Value = exc.Value, exc.Type
			}
			packet.Interfaces = append(packet.Interfaces, exc)
			packet.Culprit = err.Error()
		} else {
			currentStacktrace := raven.NewStacktrace(stConfig.Skip, stConfig.Context, stConfig.InAppPrefixes)
			if currentStacktrace != nil {
				packet.Interfaces = append(packet.Interfaces, currentStacktrace)
			}
		}
	} else {
		// set the culprit even when the stack trace is disabled, as long as we have an error
		if err, ok := df.getError(); ok {
			packet.Culprit = err.Error()
		}
	}

	// set other fields
	dataExtra := hook.formatExtraData(df)
	if packet.Extra == nil {
		packet.Extra = dataExtra
	} else {
		for k, v := range dataExtra {
			packet.Extra[k] = v
		}
	}

	_, errCh := hook.client.Capture(packet, nil)

	if hook.asynchronous {
		// Our use of hook.mu guarantees that we are following the WaitGroup rule of
		// not calling Add in parallel with Wait.
		hook.wg.Add(1)
		go func() {
			if err := <-errCh; err != nil {
				fmt.Println(err)
			}
			hook.wg.Done()
		}()
		return nil
	} else if timeout := hook.Timeout; timeout == 0 {
		return nil
	} else {
		timeoutCh := time.After(timeout)
		select {
		case err := <-errCh:
			return err
		case <-timeoutCh:
			return fmt.Errorf("no response from sentry server in %s", timeout)
		}
	}
}

// Flush waits for the log queue to empty. This function only does anything in
// asynchronous mode.
func (hook *SentryHook) Flush() {
	if !hook.asynchronous {
		return
	}
	hook.mu.Lock() // Claim exclusive access; any logging goroutines will block until the flush completes
	defer hook.mu.Unlock()

	hook.wg.Wait()
}

func (hook *SentryHook) findStacktrace(err error) *raven.Stacktrace {
	var stacktrace *raven.Stacktrace
	var stackErr errors.StackTrace
	for err != nil {
		// Find the earliest *raven.Stacktrace, or error.StackTrace
		if tracer, ok := err.(Stacktracer); ok {
			stacktrace = tracer.GetStacktrace()
			stackErr = nil
		} else if tracer, ok := err.(pkgErrorStackTracer); ok {
			stacktrace = nil
			stackErr = tracer.StackTrace()
		}
		if cause, ok := err.(causer); ok {
			err = cause.Cause()
		} else {
			break
		}
	}
	if stackErr != nil {
		stacktrace = hook.convertStackTrace(stackErr)
	}
	return stacktrace
}

// convertStackTrace converts an errors.StackTrace into a natively consumable
// *raven.Stacktrace
func (hook *SentryHook) convertStackTrace(st errors.StackTrace) *raven.Stacktrace {
	stConfig := &hook.StacktraceConfiguration
	stFrames := []errors.Frame(st)
	frames := make([]*raven.StacktraceFrame, 0, len(stFrames))
	for i := range stFrames {
		pc := uintptr(stFrames[i])
		fn := runtime.FuncForPC(pc)
		file, line := fn.FileLine(pc)
		frame := raven.NewStacktraceFrame(pc, file, line, stConfig.Context, stConfig.InAppPrefixes)
		if frame != nil {
			frames = append(frames, frame)
		}
	}

	// Sentry wants the frames with the oldest first, so reverse them
	for i, j := 0, len(frames)-1; i < j; i, j = i+1, j-1 {
		frames[i], frames[j] = frames[j], frames[i]
	}
	return &raven.Stacktrace{Frames: frames}
}

// Levels returns the available logging levels.
func (hook *SentryHook) Levels() []logrus.Level {
	return hook.levels
}

// SetRelease sets release tag.
func (hook *SentryHook) SetRelease(release string) {
	hook.client.SetRelease(release)
}

// SetEnvironment sets environment tag.
func (hook *SentryHook) SetEnvironment(environment string) {
	hook.client.SetEnvironment(environment)
}

// SetServerName sets server_name tag.
func (hook *SentryHook) SetServerName(serverName string) {
	hook.serverName = serverName
}

// AddIgnore adds field name to ignore.
func (hook *SentryHook) AddIgnore(name string) {
	hook.ignoreFields[name] = struct{}{}
}

// AddExtraFilter adds a custom filter function.
func (hook *SentryHook) AddExtraFilter(name string, fn func(interface{}) interface{}) {
	hook.extraFilters[name] = fn
}

func (hook *SentryHook) formatExtraData(df *dataField) (result map[string]interface{}) {
	// create a map for passing to Sentry's extra data
	result = make(map[string]interface{}, df.len())
	for k, v := range df.data {
		if df.isOmit(k) {
			continue // skip already used special fields
		}
		if _, ok := hook.ignoreFields[k]; ok {
			continue
		}

		if fn, ok := hook.extraFilters[k]; ok {
			v = fn(v) // apply custom filter
		} else {
			v = formatData(v) // use default formatter
		}
		result[k] = v
	}
	return result
}

// formatData returns value as a suitable format.
func formatData(value interface{}) (formatted interface{}) {
	switch value := value.(type) {
	case json.Marshaler:
		return value
	case error:
		return value.Error()
	case fmt.Stringer:
		return value.String()
	default:
		return value
	}
}
<commit_msg>Fix grouping and use a useful culprit with new SwitchExceptionTypeAndMessage option (#58)<commit_after>package logrus_sentry

import (
	"encoding/json"
	"fmt"
	"runtime"
	"sync"
	"time"

	"github.com/getsentry/raven-go"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
)

var (
	severityMap = map[logrus.Level]raven.Severity{
		logrus.DebugLevel: raven.DEBUG,
		logrus.InfoLevel:  raven.INFO,
		logrus.WarnLevel:  raven.WARNING,
		logrus.ErrorLevel: raven.ERROR,
		logrus.FatalLevel: raven.FATAL,
		logrus.PanicLevel: raven.FATAL,
	}
)

// SentryHook delivers logs to a sentry server.
type SentryHook struct {
	// Timeout sets the time to wait for a delivery error from the sentry server.
	// If this is set to zero the server will not wait for any response and will
	// consider the message correctly sent.
	//
	// This is ignored for asynchronous hooks. If you want to set a timeout when
	// using an async hook (to bound the length of time that hook.Flush can take),
	// you probably want to create your own raven.Client and set
	// ravenClient.Transport.(*raven.HTTPTransport).Client.Timeout to set a
	// timeout on the underlying HTTP request instead.
	Timeout                 time.Duration
	StacktraceConfiguration StackTraceConfiguration

	client *raven.Client
	levels []logrus.Level

	serverName   string
	ignoreFields map[string]struct{}
	extraFilters map[string]func(interface{}) interface{}

	asynchronous bool

	mu sync.RWMutex
	wg sync.WaitGroup
}

// The Stacktracer interface allows an error type to return a raven.Stacktrace.
type Stacktracer interface {
	GetStacktrace() *raven.Stacktrace
}

type causer interface {
	Cause() error
}

type pkgErrorStackTracer interface {
	StackTrace() errors.StackTrace
}

// StackTraceConfiguration allows for configuring stacktraces
type StackTraceConfiguration struct {
	// whether stacktraces should be enabled
	Enable bool
	// the level at which to start capturing stacktraces
	Level logrus.Level
	// how many stack frames to skip before stacktrace starts recording
	Skip int
	// the number of lines to include around a stack frame for context
	Context int
	// the prefixes that will be matched against the stack frame.
	// if the stack frame's package matches one of these prefixes
	// sentry will identify the stack frame as "in_app"
	InAppPrefixes []string
	// whether sending exception type should be enabled.
	SendExceptionType bool
	// whether the exception type and message should be switched.
	SwitchExceptionTypeAndMessage bool
}

// NewSentryHook creates a hook to be added to an instance of logger
// and initializes the raven client.
// This method sets the timeout to 100 milliseconds.
func NewSentryHook(DSN string, levels []logrus.Level) (*SentryHook, error) {
	client, err := raven.New(DSN)
	if err != nil {
		return nil, err
	}
	return NewWithClientSentryHook(client, levels)
}

// NewWithTagsSentryHook creates a hook with tags to be added to an instance
// of logger and initializes the raven client. This method sets the timeout to
// 100 milliseconds.
func NewWithTagsSentryHook(DSN string, tags map[string]string, levels []logrus.Level) (*SentryHook, error) {
	client, err := raven.NewWithTags(DSN, tags)
	if err != nil {
		return nil, err
	}
	return NewWithClientSentryHook(client, levels)
}

// NewWithClientSentryHook creates a hook using an initialized raven client.
// This method sets the timeout to 100 milliseconds.
func NewWithClientSentryHook(client *raven.Client, levels []logrus.Level) (*SentryHook, error) {
	return &SentryHook{
		Timeout: 100 * time.Millisecond,
		StacktraceConfiguration: StackTraceConfiguration{
			Enable:            false,
			Level:             logrus.ErrorLevel,
			Skip:              5,
			Context:           0,
			InAppPrefixes:     nil,
			SendExceptionType: true,
		},
		client:       client,
		levels:       levels,
		ignoreFields: make(map[string]struct{}),
		extraFilters: make(map[string]func(interface{}) interface{}),
	}, nil
}

// NewAsyncSentryHook creates a hook same as NewSentryHook, but in asynchronous
// mode.
func NewAsyncSentryHook(DSN string, levels []logrus.Level) (*SentryHook, error) {
	hook, err := NewSentryHook(DSN, levels)
	return setAsync(hook), err
}

// NewAsyncWithTagsSentryHook creates a hook same as NewWithTagsSentryHook, but
// in asynchronous mode.
func NewAsyncWithTagsSentryHook(DSN string, tags map[string]string, levels []logrus.Level) (*SentryHook, error) {
	hook, err := NewWithTagsSentryHook(DSN, tags, levels)
	return setAsync(hook), err
}

// NewAsyncWithClientSentryHook creates a hook same as NewWithClientSentryHook,
// but in asynchronous mode.
func NewAsyncWithClientSentryHook(client *raven.Client, levels []logrus.Level) (*SentryHook, error) {
	hook, err := NewWithClientSentryHook(client, levels)
	return setAsync(hook), err
}

func setAsync(hook *SentryHook) *SentryHook {
	if hook == nil {
		return nil
	}
	hook.asynchronous = true
	return hook
}

// Fire is called when an event should be sent to sentry
// Special fields that sentry uses to give more information to the server
// are extracted from entry.Data (if they are found)
// These fields are: error, logger, server_name, http_request, tags
func (hook *SentryHook) Fire(entry *logrus.Entry) error {
	hook.mu.RLock() // Allow multiple go routines to log simultaneously
	defer hook.mu.RUnlock()
	packet := raven.NewPacket(entry.Message)
	packet.Timestamp = raven.Timestamp(entry.Time)
	packet.Level = severityMap[entry.Level]
	packet.Platform = "go"

	df := newDataField(entry.Data)

	// set special fields
	if hook.serverName != "" {
		packet.ServerName = hook.serverName
	}
	if logger, ok := df.getLogger(); ok {
		packet.Logger = logger
	}
	if serverName, ok := df.getServerName(); ok {
		packet.ServerName = serverName
	}
	if eventID, ok := df.getEventID(); ok {
		packet.EventID = eventID
	}
	if tags, ok := df.getTags(); ok {
		packet.Tags = tags
	}
	if fingerprint, ok := df.getFingerprint(); ok {
		packet.Fingerprint = fingerprint
	}
	if req, ok := df.getHTTPRequest(); ok {
		packet.Interfaces = append(packet.Interfaces, req)
	}
	if user, ok := df.getUser(); ok {
		packet.Interfaces = append(packet.Interfaces, user)
	}

	// set stacktrace data
	stConfig := &hook.StacktraceConfiguration
	if stConfig.Enable && entry.Level <= stConfig.Level {
		if err, ok := df.getError(); ok {
			var currentStacktrace *raven.Stacktrace
			currentStacktrace = hook.findStacktrace(err)
			if currentStacktrace == nil {
				currentStacktrace = raven.NewStacktrace(stConfig.Skip, stConfig.Context, stConfig.InAppPrefixes)
			}
			exc := raven.NewException(errors.Cause(err), currentStacktrace)
			if !stConfig.SendExceptionType {
				exc.Type = ""
			}
			if stConfig.SwitchExceptionTypeAndMessage {
				packet.Interfaces = append(packet.Interfaces, currentStacktrace)
				packet.Culprit = exc.Type + ": " + currentStacktrace.Culprit()
			} else {
				packet.Interfaces = append(packet.Interfaces, exc)
				packet.Culprit = err.Error()
			}
		} else {
			currentStacktrace := raven.NewStacktrace(stConfig.Skip, stConfig.Context, stConfig.InAppPrefixes)
			if currentStacktrace != nil {
				packet.Interfaces = append(packet.Interfaces, currentStacktrace)
			}
		}
	} else {
		// set the culprit even when the stack trace is disabled, as long as we have an error
		if err, ok := df.getError(); ok {
			packet.Culprit = err.Error()
		}
	}

	// set other fields
	dataExtra := hook.formatExtraData(df)
	if packet.Extra == nil {
		packet.Extra = dataExtra
	} else {
		for k, v := range dataExtra {
			packet.Extra[k] = v
		}
	}

	_, errCh := hook.client.Capture(packet, nil)

	if hook.asynchronous {
		// Our use of hook.mu guarantees that we are following the WaitGroup rule of
		// not calling Add in parallel with Wait.
		hook.wg.Add(1)
		go func() {
			if err := <-errCh; err != nil {
				fmt.Println(err)
			}
			hook.wg.Done()
		}()
		return nil
	} else if timeout := hook.Timeout; timeout == 0 {
		return nil
	} else {
		timeoutCh := time.After(timeout)
		select {
		case err := <-errCh:
			return err
		case <-timeoutCh:
			return fmt.Errorf("no response from sentry server in %s", timeout)
		}
	}
}

// Flush waits for the log queue to empty. This function only does anything in
// asynchronous mode.
func (hook *SentryHook) Flush() {
	if !hook.asynchronous {
		return
	}
	hook.mu.Lock() // Claim exclusive access; any logging goroutines will block until the flush completes
	defer hook.mu.Unlock()

	hook.wg.Wait()
}

func (hook *SentryHook) findStacktrace(err error) *raven.Stacktrace {
	var stacktrace *raven.Stacktrace
	var stackErr errors.StackTrace
	for err != nil {
		// Find the earliest *raven.Stacktrace, or error.StackTrace
		if tracer, ok := err.(Stacktracer); ok {
			stacktrace = tracer.GetStacktrace()
			stackErr = nil
		} else if tracer, ok := err.(pkgErrorStackTracer); ok {
			stacktrace = nil
			stackErr = tracer.StackTrace()
		}
		if cause, ok := err.(causer); ok {
			err = cause.Cause()
		} else {
			break
		}
	}
	if stackErr != nil {
		stacktrace = hook.convertStackTrace(stackErr)
	}
	return stacktrace
}

// convertStackTrace converts an errors.StackTrace into a natively consumable
// *raven.Stacktrace
func (hook *SentryHook) convertStackTrace(st errors.StackTrace) *raven.Stacktrace {
	stConfig := &hook.StacktraceConfiguration
	stFrames := []errors.Frame(st)
	frames := make([]*raven.StacktraceFrame, 0, len(stFrames))
	for i := range stFrames {
		pc := uintptr(stFrames[i])
		fn := runtime.FuncForPC(pc)
		file, line := fn.FileLine(pc)
		frame := raven.NewStacktraceFrame(pc, file, line, stConfig.Context, stConfig.InAppPrefixes)
		if frame != nil {
			frames = append(frames, frame)
		}
	}

	// Sentry wants the frames with the oldest first, so reverse them
	for i, j := 0, len(frames)-1; i < j; i, j = i+1, j-1 {
		frames[i], frames[j] = frames[j], frames[i]
	}
	return &raven.Stacktrace{Frames: frames}
}

// Levels returns the available logging levels.
func (hook *SentryHook) Levels() []logrus.Level {
	return hook.levels
}

// SetRelease sets release tag.
func (hook *SentryHook) SetRelease(release string) {
	hook.client.SetRelease(release)
}

// SetEnvironment sets environment tag.
func (hook *SentryHook) SetEnvironment(environment string) {
	hook.client.SetEnvironment(environment)
}

// SetServerName sets server_name tag.
func (hook *SentryHook) SetServerName(serverName string) {
	hook.serverName = serverName
}

// AddIgnore adds field name to ignore.
func (hook *SentryHook) AddIgnore(name string) {
	hook.ignoreFields[name] = struct{}{}
}

// AddExtraFilter adds a custom filter function.
func (hook *SentryHook) AddExtraFilter(name string, fn func(interface{}) interface{}) {
	hook.extraFilters[name] = fn
}

func (hook *SentryHook) formatExtraData(df *dataField) (result map[string]interface{}) {
	// create a map for passing to Sentry's extra data
	result = make(map[string]interface{}, df.len())
	for k, v := range df.data {
		if df.isOmit(k) {
			continue // skip already used special fields
		}
		if _, ok := hook.ignoreFields[k]; ok {
			continue
		}

		if fn, ok := hook.extraFilters[k]; ok {
			v = fn(v) // apply custom filter
		} else {
			v = formatData(v) // use default formatter
		}
		result[k] = v
	}
	return result
}

// formatData returns value as a suitable format.
func formatData(value interface{}) (formatted interface{}) {
	switch value := value.(type) {
	case json.Marshaler:
		return value
	case error:
		return value.Error()
	case fmt.Stringer:
		return value.String()
	default:
		return value
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"strconv"
	"time"

	"github.com/gopherjs/gopherjs/js"
	"github.com/gopherjs/jquery"
	"github.com/peternoyes/dodo-sim"
)

func runLogic(s *dodosim.SimulatorSync) {
	jQuery("#runButton").On(jquery.CLICK, func() {
		go func() {
			if !saveProjectCode() {
				return
			}

			fmt.Println("Compile Initiated...")

			setStatus("Compiling...", "bg-info")

			data, id, err := compileCode()
			if err != nil {
				setStatus(err.Error(), "bg-danger")
				return
			}

			setStatus("Loading Simulator...", "bg-success")

			fram = data

			firmwareBytes, err := getFirmware(version)
			if err != nil {
				setStatus(err.Error(), "bg-danger")
				return
			}

			s.SwitchFram(firmwareBytes, fram)

			gamelink := js.Global.Get("document").Get("location").Get("origin").String()
			gamelink += "/?code="
			gamelink += id

			jQuery("#gamelink").SetText(gamelink)

			jQuery("#simModal").Call("modal")

			runSimulator(s)

			setStatus("Success! Simulator Running.", "bg-success")
		}()
	})

	jQuery("#simModal").On("hidden.bs.modal", func() {
		go func() {
			fmt.Println("About to stop simulator")
			stopSimulator()
			setStatus("Simulator Stopped.", "bg-success")
		}()
	})

	jQuery("#resetButton").On(jquery.CLICK, func() {
		go func() {
			s.Cpu.Reset(s.Bus)
		}()
	})

	jQuery("#copyButton").On(jquery.CLICK, func() {
		go func() {
			parent := jQuery("#simModal")
			temp := jQuery(js.Global.Get("document").Call("createElement", "input"))
			parent.Call("append", temp)
			temp.SetVal(jQuery("#gamelink").Text()).Select()
			js.Global.Get("document").Call("execCommand", "copy")
			temp.Remove()
		}()
	})

	jQuery("#muteButton").On(jquery.CLICK, func() {
		go func() {
			speaker.ToggleMute()
			jQuery("#muteButton").ToggleClass("active")
		}()
	})

	s.CyclesPerFrame = func(cycles uint64) {
		jQuery("#cycles").SetText(strconv.Itoa(int(cycles)))
	}
}

func runSimulator(s *dodosim.SimulatorSync) {
	stop = false

	speaker.Enable()

	keyState := make(map[int]bool)
	buttonState := make(map[string]bool)
	buttons := []string{"upButton", "leftButton", "rightButton", "downButton", "aButton", "bButton"}

	downHandler := func(event *js.Object) {
		k := event.Get("keyCode").Int()
		if event.Get("ctrlKey").Bool() || k == 91 || k == 67 { // Allow Ctrl+C
			return
		}

		event.Call("preventDefault")
		keyState[k] = true
	}

	upHandler := func(event *js.Object) {
		k := event.Get("keyCode").Int()
		if event.Get("ctrlKey").Bool() || k == 91 || k == 67 {
			return
		}

		event.Call("preventDefault")
		keyState[k] = false
	}

	buttonDownHandler := func(event *js.Object) {
		go func() {
			event.Call("preventDefault")
			tgt := event.Get("target")
			if tgt.Get("tagName").String() == "SPAN" {
				tgt = tgt.Get("parentNode")
			}

			id := tgt.Get("id").String()
			buttonState[id] = true
		}()
	}

	buttonUpHandler := func(event *js.Object) {
		go func() {
			tgt := event.Get("target")
			if tgt.Get("tagName").String() == "SPAN" {
				tgt = tgt.Get("parentNode")
			}

			id := tgt.Get("id").String()
			buttonState[id] = false
		}()
	}

	js.Global.Call("addEventListener", "keydown", downHandler)
	js.Global.Call("addEventListener", "keyup", upHandler)

	for _, s := range buttons {
		jQuery("#"+s).On(jquery.MOUSEDOWN, func(event *js.Object) { buttonDownHandler(event) })
		jQuery("#"+s).On(jquery.MOUSEUP, func(event *js.Object) { buttonUpHandler(event) })

		jQuery("#"+s).On(jquery.TOUCHSTART, func(event *js.Object) { buttonDownHandler(event) })
		jQuery("#"+s).On(jquery.TOUCHEND, func(event *js.Object) { buttonUpHandler(event) })
	}

	// Measure 2 seconds worth, to figure out delay
	start := time.Now()
	for i := 0; i < 40; i++ {
		s.PumpClock("")
	}
	elapsed := time.Since(start)
	fmt.Println(elapsed)

	// Calculate the delay necessary to achieve 20FPS
	delay := 1.0
	seconds := elapsed.Seconds()
	if seconds < 1.5 {
		delay = ((2.0 - seconds) / 40.0) * 1000.0
	}

	fmt.Println("Delay of :", delay)

	Every(time.Millisecond*time.Duration(delay), func() bool {
		k := ""
		if keyState[37] || buttonState["leftButton"] {
			k += "L"
		}
		if keyState[38] || buttonState["upButton"] {
			k += "U"
		}
		if keyState[39] || buttonState["rightButton"] {
			k += "R"
		}
		if keyState[40] || buttonState["downButton"] {
			k += "D"
		}
		if keyState[65] || buttonState["aButton"] {
			k += "A"
		}
		if keyState[66] || buttonState["bButton"] {
			k += "B"
		}

		s.PumpClock(k)

		if stop {
			js.Global.Call("removeEventListener", "keydown", downHandler)
			js.Global.Call("removeEventListener", "keyup", upHandler)

			for _, s := range buttons {
				jQuery("#"+s).Off(jquery.MOUSEDOWN, nil)
				jQuery("#"+s).Off(jquery.MOUSEUP, nil)
			}
		}

		return !stop
	})
}

func stopSimulator() {
	speaker.Disable()
	stop = true
}

func Every(duration time.Duration, fn func() bool) {
	time.AfterFunc(duration, func() {
		if !fn() {
			return
		}
		Every(duration, fn)
	})
}
<commit_msg>Used space indicator<commit_after>package main

import (
	"fmt"
	"strconv"
	"time"

	"github.com/gopherjs/gopherjs/js"
	"github.com/gopherjs/jquery"
	"github.com/peternoyes/dodo-sim"
)

func getUsedSpace(data []byte) int {
	for i := 8191; i >= 0; i-- {
		if data[i] != 0 {
			return (i * 100) / 8192
		}
	}

	return 0
}

func runLogic(s *dodosim.SimulatorSync) {
	jQuery("#runButton").On(jquery.CLICK, func() {
		go func() {
			if !saveProjectCode() {
				return
			}

			fmt.Println("Compile Initiated...")

			setStatus("Compiling...", "bg-info")

			data, id, err := compileCode()
			if err != nil {
				setStatus(err.Error(), "bg-danger")
				return
			}

			usedSpace := getUsedSpace(data)

			setStatus("Loading Simulator...", "bg-success")

			fram = data

			firmwareBytes, err := getFirmware(version)
			if err != nil {
				setStatus(err.Error(), "bg-danger")
				return
			}

			s.SwitchFram(firmwareBytes, fram)

			gamelink := js.Global.Get("document").Get("location").Get("origin").String()
			gamelink += "/?code="
			gamelink += id

			jQuery("#gamelink").SetText(gamelink)

			jQuery("#simModal").Call("modal")

			runSimulator(s)

			setStatus("Success! Simulator Running.\nUsed Space: "+strconv.Itoa(usedSpace)+"%", "bg-success")
		}()
	})

	jQuery("#simModal").On("hidden.bs.modal", func() {
		go func() {
			fmt.Println("About to stop simulator")
			stopSimulator()
			setStatus("Simulator Stopped.", "bg-success")
		}()
	})

	jQuery("#resetButton").On(jquery.CLICK, func() {
		go func() {
			s.Cpu.Reset(s.Bus)
		}()
	})

	jQuery("#copyButton").On(jquery.CLICK, func() {
		go func() {
			parent := jQuery("#simModal")
			temp := jQuery(js.Global.Get("document").Call("createElement", "input"))
			parent.Call("append", temp)
			temp.SetVal(jQuery("#gamelink").Text()).Select()
			js.Global.Get("document").Call("execCommand", "copy")
			temp.Remove()
		}()
	})

	jQuery("#muteButton").On(jquery.CLICK, func() {
		go func() {
			speaker.ToggleMute()
			jQuery("#muteButton").ToggleClass("active")
		}()
	})

	s.CyclesPerFrame = func(cycles uint64) {
		jQuery("#cycles").SetText(strconv.Itoa(int(cycles)))
	}
}

func runSimulator(s *dodosim.SimulatorSync) {
	stop = false

	speaker.Enable()

	keyState := make(map[int]bool)
	buttonState := make(map[string]bool)
	buttons := []string{"upButton", "leftButton", "rightButton", "downButton", "aButton", "bButton"}

	downHandler := func(event *js.Object) {
		k := event.Get("keyCode").Int()
		if event.Get("ctrlKey").Bool() || k == 91 || k == 67 { // Allow Ctrl+C
			return
		}

		event.Call("preventDefault")
		keyState[k] = true
	}

	upHandler := func(event *js.Object) {
		k := event.Get("keyCode").Int()
		if event.Get("ctrlKey").Bool() || k == 91 || k == 67 {
			return
		}

		event.Call("preventDefault")
		keyState[k] = false
	}

	buttonDownHandler := func(event *js.Object) {
		go func() {
			event.Call("preventDefault")
			tgt := event.Get("target")
			if tgt.Get("tagName").String() == "SPAN" {
				tgt = tgt.Get("parentNode")
			}

			id := tgt.Get("id").String()
			buttonState[id] = true
		}()
	}

	buttonUpHandler := func(event *js.Object) {
		go func() {
			tgt := event.Get("target")
			if tgt.Get("tagName").String() == "SPAN" {
				tgt = tgt.Get("parentNode")
			}

			id := tgt.Get("id").String()
			buttonState[id] = false
		}()
	}

	js.Global.Call("addEventListener", "keydown", downHandler)
	js.Global.Call("addEventListener", "keyup", upHandler)

	for _, s := range buttons {
		jQuery("#"+s).On(jquery.MOUSEDOWN, func(event *js.Object) { buttonDownHandler(event) })
		jQuery("#"+s).On(jquery.MOUSEUP, func(event *js.Object) { buttonUpHandler(event) })

		jQuery("#"+s).On(jquery.TOUCHSTART, func(event *js.Object) { buttonDownHandler(event) })
		jQuery("#"+s).On(jquery.TOUCHEND, func(event *js.Object) { buttonUpHandler(event) })
	}

	// Measure 2 seconds worth, to figure out delay
	start := time.Now()
	for i := 0; i < 40; i++ {
		s.PumpClock("")
	}
	elapsed := time.Since(start)
	fmt.Println(elapsed)

	// Calculate the delay necessary to achieve 20FPS
	delay := 1.0
	seconds := elapsed.Seconds()
	if seconds < 1.5 {
		delay = ((2.0 - seconds) / 40.0) * 1000.0
	}

	fmt.Println("Delay of :", delay)

	Every(time.Millisecond*time.Duration(delay), func() bool {
		k := ""
		if keyState[37] || buttonState["leftButton"] {
			k += "L"
		}
		if keyState[38] || buttonState["upButton"] {
			k += "U"
		}
		if keyState[39] || buttonState["rightButton"] {
			k += "R"
		}
		if keyState[40] || buttonState["downButton"] {
			k += "D"
		}
		if keyState[65] || buttonState["aButton"] {
			k += "A"
		}
		if keyState[66] || buttonState["bButton"] {
			k += "B"
		}

		s.PumpClock(k)

		if stop {
			js.Global.Call("removeEventListener", "keydown", downHandler)
			js.Global.Call("removeEventListener", "keyup", upHandler)

			for _, s := range buttons {
				jQuery("#"+s).Off(jquery.MOUSEDOWN, nil)
				jQuery("#"+s).Off(jquery.MOUSEUP, nil)
			}
		}

		return !stop
	})
}

func stopSimulator() {
	speaker.Disable()
	stop = true
}

func Every(duration time.Duration, fn func() bool) {
	time.AfterFunc(duration, func() {
		if !fn() {
			return
		}
		Every(duration, fn)
	})
}
<|endoftext|>


<commit_before>package ethminer

import (
	"bytes"
	"github.com/ethereum/eth-go/ethchain"
	"github.com/ethereum/eth-go/ethutil"
	"github.com/ethereum/eth-go/ethwire"
	"log"
)

type Miner struct {
	pow       ethchain.PoW
	ethereum  ethchain.EthManager
	coinbase  []byte
	reactChan chan ethutil.React
	txs       []*ethchain.Transaction
	uncles    []*ethchain.Block
	block     *ethchain.Block
	powChan   chan []byte
	quitChan  chan ethutil.React
}

func NewDefaultMiner(coinbase []byte, ethereum ethchain.EthManager) Miner {
	reactChan := make(chan ethutil.React, 1) // This is the channel that receives 'updates' when ever a new transaction or block comes in
	powChan := make(chan []byte, 1)          // This is the channel that receives valid sha hases for a given block
	quitChan := make(chan ethutil.React, 1)  // This is the channel that can exit the miner thread

	ethereum.Reactor().Subscribe("newBlock", reactChan)
	ethereum.Reactor().Subscribe("newTx:post", reactChan)

	// We need the quit chan to be a Reactor event.
	// The POW search method is actually blocking and if we don't
	// listen to the reactor events inside of the pow itself
	// The miner overseer will never get the reactor events themselves
	// Only after the miner will find the sha
	ethereum.Reactor().Subscribe("newBlock", quitChan)
	ethereum.Reactor().Subscribe("newTx:post", quitChan)

	miner := Miner{
		pow:       &ethchain.EasyPow{},
		ethereum:  ethereum,
		coinbase:  coinbase,
		reactChan: reactChan,
		powChan:   powChan,
		quitChan:  quitChan,
	}

	// Insert initial TXs in our little miner 'pool'
	miner.txs = ethereum.TxPool().Flush()
	miner.block = ethereum.BlockChain().NewBlock(miner.coinbase, miner.txs)

	return miner
}
func (miner *Miner) Start() {
	// Prepare inital block
	miner.ethereum.StateManager().Prepare(miner.block.State(), miner.block.State())
	go func() { miner.listener() }()
}
func (miner *Miner) listener() {
	for {
		select {
		case chanMessage := <-miner.reactChan:
			if block, ok := chanMessage.Resource.(*ethchain.Block); ok {
				log.Println("[MINER] Got new block via Reactor")
				if bytes.Compare(miner.ethereum.BlockChain().CurrentBlock.Hash(), block.Hash()) == 0 {
					// TODO: Perhaps continue mining to get some uncle rewards
					log.Println("[MINER] New top block found resetting state")

					// Filter out which Transactions we have that were not in this block
					var newtxs []*ethchain.Transaction
					for _, tx := range miner.txs {
						found := false
						for _, othertx := range block.Transactions() {
							if bytes.Compare(tx.Hash(), othertx.Hash()) == 0 {
								found = true
							}
						}
						if found == false {
							newtxs = append(newtxs, tx)
						}
					}
					miner.txs = newtxs

					// Setup a fresh state to mine on
					miner.block = miner.ethereum.BlockChain().NewBlock(miner.coinbase, miner.txs)

				} else {
					if bytes.Compare(block.PrevHash, miner.ethereum.BlockChain().CurrentBlock.PrevHash) == 0 {
						log.Println("[MINER] Adding uncle block")
						miner.uncles = append(miner.uncles, block)
						miner.ethereum.StateManager().Prepare(miner.block.State(), miner.block.State())
					}
				}
			}

			if tx, ok := chanMessage.Resource.(*ethchain.Transaction); ok {
				//log.Println("[MINER] Got new transaction from Reactor", tx)
				found := false
				for _, ctx := range miner.txs {
					if found = bytes.Compare(ctx.Hash(), tx.Hash()) == 0; found {
						break
					}

				}
				if found == false {
					//log.Println("[MINER] We did not know about this transaction, adding")
					miner.txs = append(miner.txs, tx)
					miner.block = miner.ethereum.BlockChain().NewBlock(miner.coinbase, miner.txs)
					miner.block.SetTransactions(miner.txs)
				} else {
					//log.Println("[MINER] We already had this transaction, ignoring")
				}
			}
		default:
			log.Println("[MINER] Mining on block. Includes", len(miner.txs), "transactions")

			// Apply uncles
			if len(miner.uncles) > 0 {
				miner.block.SetUncles(miner.uncles)
			}

			// FIXME @ maranh, first block doesn't need this. Everything after the first block does.
			// Please check and fix
			miner.ethereum.StateManager().Prepare(miner.block.State(), miner.block.State())
			// Apply all transactions to the block
			miner.ethereum.StateManager().ApplyTransactions(miner.block, miner.block.Transactions())
			miner.ethereum.StateManager().AccumelateRewards(miner.block)

			// Search the nonce
			//log.Println("[MINER] Initialision complete, starting mining")
			miner.block.Nonce = miner.pow.Search(miner.block, miner.quitChan)
			if miner.block.Nonce != nil {
				miner.ethereum.StateManager().PrepareDefault(miner.block)
				err := miner.ethereum.StateManager().ProcessBlock(miner.block, true)
				if err != nil {
					log.Println(err)
					miner.txs = []*ethchain.Transaction{} // Move this somewhere neat
					miner.block = miner.ethereum.BlockChain().NewBlock(miner.coinbase, miner.txs)
				} else {

					/*
						// XXX @maranh This is already done in the state manager, why a 2nd time?
						if !miner.ethereum.StateManager().Pow.Verify(miner.block.HashNoNonce(), miner.block.Difficulty, miner.block.Nonce) {
							log.Printf("Second stage verification error: Block's nonce is invalid (= %v)\n", ethutil.Hex(miner.block.Nonce))
						}
					*/
					miner.ethereum.Broadcast(ethwire.MsgBlockTy, []interface{}{miner.block.Value().Val})
					log.Printf("[MINER] 🔨  Mined block %x\n", miner.block.Hash())

					miner.txs = []*ethchain.Transaction{} // Move this somewhere neat
					miner.block = miner.ethereum.BlockChain().NewBlock(miner.coinbase, miner.txs)
				}
			}
		}
	}
}
<commit_msg>listen to pre instead of post<commit_after>package ethminer

import (
	"bytes"
	"github.com/ethereum/eth-go/ethchain"
	"github.com/ethereum/eth-go/ethutil"
	"github.com/ethereum/eth-go/ethwire"
	"log"
)

type Miner struct {
	pow       ethchain.PoW
	ethereum  ethchain.EthManager
	coinbase  []byte
	reactChan chan ethutil.React
	txs       []*ethchain.Transaction
	uncles    []*ethchain.Block
	block     *ethchain.Block
	powChan   chan []byte
	quitChan  chan ethutil.React
}

func NewDefaultMiner(coinbase []byte, ethereum ethchain.EthManager) Miner {
	reactChan := make(chan ethutil.React, 1) // This is the channel that receives 'updates' when ever a new transaction or block comes in
	powChan := make(chan []byte, 1)          // This is the channel that receives valid sha hases for a given block
	quitChan := make(chan ethutil.React, 1)  // This is the channel that can exit the miner thread

	ethereum.Reactor().Subscribe("newBlock", reactChan)
	ethereum.Reactor().Subscribe("newTx:pre", reactChan)

	// We need the quit chan to be a Reactor event.
	// The POW search method is actually blocking and if we don't
	// listen to the reactor events inside of the pow itself
	// The miner overseer will never get the reactor events themselves
	// Only after the miner will find the sha
	ethereum.Reactor().Subscribe("newBlock", quitChan)
	ethereum.Reactor().Subscribe("newTx:pre", quitChan)

	miner := Miner{
		pow:       &ethchain.EasyPow{},
		ethereum:  ethereum,
		coinbase:  coinbase,
		reactChan: reactChan,
		powChan:   powChan,
		quitChan:  quitChan,
	}

	// Insert initial TXs in our little miner 'pool'
	miner.txs = ethereum.TxPool().Flush()
	miner.block = ethereum.BlockChain().NewBlock(miner.coinbase, miner.txs)

	return miner
}
func (miner *Miner) Start() {
	// Prepare inital block
	miner.ethereum.StateManager().Prepare(miner.block.State(), miner.block.State())
	go func() { miner.listener() }()
}
func (miner *Miner) listener() {
	for {
		select {
		case chanMessage := <-miner.reactChan:
			if block, ok := chanMessage.Resource.(*ethchain.Block); ok {
				log.Println("[MINER] Got new block via Reactor")
				if bytes.Compare(miner.ethereum.BlockChain().CurrentBlock.Hash(), block.Hash()) == 0 {
					// TODO: Perhaps continue mining to get some uncle rewards
					log.Println("[MINER] New top block found resetting state")

					// Filter out which Transactions we have that were not in this block
					var newtxs []*ethchain.Transaction
					for _, tx := range miner.txs {
						found := false
						for _, othertx := range block.Transactions() {
							if bytes.Compare(tx.Hash(), othertx.Hash()) == 0 {
								found = true
							}
						}
						if found == false {
							newtxs = append(newtxs, tx)
						}
					}
					miner.txs = newtxs

					// Setup a fresh state to mine on
					miner.block = miner.ethereum.BlockChain().NewBlock(miner.coinbase, miner.txs)

				} else {
					if bytes.Compare(block.PrevHash, miner.ethereum.BlockChain().CurrentBlock.PrevHash) == 0 {
						log.Println("[MINER] Adding uncle block")
						miner.uncles = append(miner.uncles, block)
						miner.ethereum.StateManager().Prepare(miner.block.State(), miner.block.State())
					}
				}
			}

			if tx, ok := chanMessage.Resource.(*ethchain.Transaction); ok {
				//log.Println("[MINER] Got new transaction from Reactor", tx)
				found := false
				for _, ctx := range miner.txs {
					if found = bytes.Compare(ctx.Hash(), tx.Hash()) == 0; found {
						break
					}

				}
				if found == false {
					//log.Println("[MINER] We did not know about this transaction, adding")
					miner.txs = append(miner.txs, tx)
					miner.block = miner.ethereum.BlockChain().NewBlock(miner.coinbase, miner.txs)
					miner.block.SetTransactions(miner.txs)
				} else {
					//log.Println("[MINER] We already had this transaction, ignoring")
				}
			}
		default:
			log.Println("[MINER] Mining on block. Includes", len(miner.txs), "transactions")

			// Apply uncles
			if len(miner.uncles) > 0 {
				miner.block.SetUncles(miner.uncles)
			}

			// FIXME @ maranh, first block doesn't need this. Everything after the first block does.
			// Please check and fix
			miner.ethereum.StateManager().Prepare(miner.block.State(), miner.block.State())
			// Apply all transactions to the block
			miner.ethereum.StateManager().ApplyTransactions(miner.block, miner.block.Transactions())
			miner.ethereum.StateManager().AccumelateRewards(miner.block)

			// Search the nonce
			//log.Println("[MINER] Initialision complete, starting mining")
			miner.block.Nonce = miner.pow.Search(miner.block, miner.quitChan)
			if miner.block.Nonce != nil {
				miner.ethereum.StateManager().PrepareDefault(miner.block)
				err := miner.ethereum.StateManager().ProcessBlock(miner.block, true)
				if err != nil {
					log.Println(err)
					miner.txs = []*ethchain.Transaction{} // Move this somewhere neat
					miner.block = miner.ethereum.BlockChain().NewBlock(miner.coinbase, miner.txs)
				} else {

					/*
						// XXX @maranh This is already done in the state manager, why a 2nd time?
						if !miner.ethereum.StateManager().Pow.Verify(miner.block.HashNoNonce(), miner.block.Difficulty, miner.block.Nonce) {
							log.Printf("Second stage verification error: Block's nonce is invalid (= %v)\n", ethutil.Hex(miner.block.Nonce))
						}
					*/
					miner.ethereum.Broadcast(ethwire.MsgBlockTy, []interface{}{miner.block.Value().Val})
					log.Printf("[MINER] 🔨  Mined block %x\n", miner.block.Hash())

					miner.txs = []*ethchain.Transaction{} // Move this somewhere neat
					miner.block = miner.ethereum.BlockChain().NewBlock(miner.coinbase, miner.txs)
				}
			}
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2018 The Go Cloud Development Kit Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://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.

// gcmsg is a sample application that publishes messages from stdin to an
// existing topic or receives messages from an existing subscription and
// sends them to stdout. The name gcmsg is short for Go CDK Messages.
package main

import (
	"bufio"
	"context"
	"flag"
	"fmt"
	"io"
	"log"
	"os"

	"github.com/google/subcommands"
	"gocloud.dev/pubsub"
	_ "gocloud.dev/pubsub/awspubsub"
	_ "gocloud.dev/pubsub/azurepubsub"
	_ "gocloud.dev/pubsub/gcppubsub"
	_ "gocloud.dev/pubsub/rabbitpubsub"
)

type pubCmd struct{}

func (*pubCmd) Name() string     { return "pub" }
func (*pubCmd) Synopsis() string { return "Publish a message to a topic" }
func (*pubCmd) Usage() string {
	return `pub <topic URL>:
  Read messages from stdin, one per line and send them to <topic URL>.

  See https://godoc.org/gocloud.dev#hdr-URLs for more background on
  Go CDK URLs, and sub-packages under gocloud.dev/pubsub
  (https://godoc.org/gocloud.dev/pubsub#pkg-subdirectories)
  for details on the topic URL format.
`
}

func (p *pubCmd) SetFlags(f *flag.FlagSet) {
}

func (p *pubCmd) Execute(ctx context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
	if f.NArg() != 1 {
		f.Usage()
		return subcommands.ExitUsageError
	}
	topicURL := f.Arg(0)
	if err := p.pub(ctx, topicURL, os.Stdin); err != nil {
		log.Print(err)
		return subcommands.ExitFailure
	}
	return subcommands.ExitSuccess
}

func (p *pubCmd) pub(ctx context.Context, topicURL string, r io.Reader) error {
	t, err := pubsub.OpenTopic(ctx, topicURL)
	if err != nil {
		return err
	}

	// Get lines from r and send them as messages to the topic.
	fmt.Fprintf(os.Stderr, "Enter messages, one per line to be published to %q.\n", topicURL)
	scanner := bufio.NewScanner(r)
	for scanner.Scan() {
		line := scanner.Text()
		if line == "" {
			log.Printf("skipping empty message")
			continue
		}
		m := &pubsub.Message{Body: []byte(line)}
		if err := t.Send(ctx, m); err != nil {
			return err
		}
	}
	return scanner.Err()
}

type subCmd struct {
	n int
}

func (*subCmd) Name() string     { return "sub" }
func (*subCmd) Synopsis() string { return "Receive messages from a subscription" }
func (*subCmd) Usage() string {
	return `sub [-n N] <subscription URL>:
  Receive messages from <subscription> and send them to stdout, one per line.

  See https://godoc.org/gocloud.dev#hdr-URLs for more background on
  Go CDK URLs, and sub-packages under gocloud.dev/pubsub
  (https://godoc.org/gocloud.dev/pubsub#pkg-subdirectories)
  for details on the subscription URL format.
`
}

func (s *subCmd) SetFlags(f *flag.FlagSet) {
	f.IntVar(&s.n, "n", 0, "number of messages to receive, or 0 for unlimited")
}

func (s *subCmd) Execute(ctx context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
	if f.NArg() != 1 {
		f.Usage()
		return subcommands.ExitUsageError
	}
	subURL := f.Arg(0)
	if err := s.sub(ctx, subURL, os.Stdout); err != nil {
		log.Println(err)
		return subcommands.ExitFailure
	}
	return subcommands.ExitSuccess
}

func (s *subCmd) sub(ctx context.Context, subURL string, w io.Writer) error {
	sub, err := pubsub.OpenSubscription(ctx, subURL)
	if err != nil {
		return err
	}
	fmt.Fprintf(os.Stderr, "Receiving messages from %q...\n", subURL)

	// Receive messages and send them to w.
	for i := 0; s.n == 0 || i < s.n; i++ {
		m, err := sub.Receive(ctx)
		if err != nil {
			return err
		}
		fmt.Fprintf(w, "%s\n", m.Body)
		m.Ack()
	}
	return nil
}

func main() {
	subcommands.Register(subcommands.HelpCommand(), "")
	subcommands.Register(subcommands.FlagsCommand(), "")
	subcommands.Register(subcommands.CommandsCommand(), "")
	subcommands.Register(&pubCmd{}, "")
	subcommands.Register(&subCmd{}, "")
	log.SetFlags(0)
	log.SetPrefix("gcmsg: ")
	flag.Parse()
	ctx := context.Background()
	os.Exit(int(subcommands.Execute(ctx)))
}
<commit_msg>samples/gcmsg: add NATS to gcmsg sample (#1605)<commit_after>// Copyright 2018 The Go Cloud Development Kit Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://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.

// gcmsg is a sample application that publishes messages from stdin to an
// existing topic or receives messages from an existing subscription and
// sends them to stdout. The name gcmsg is short for Go CDK Messages.
package main

import (
	"bufio"
	"context"
	"flag"
	"fmt"
	"io"
	"log"
	"os"

	"github.com/google/subcommands"
	"gocloud.dev/pubsub"
	_ "gocloud.dev/pubsub/awspubsub"
	_ "gocloud.dev/pubsub/azurepubsub"
	_ "gocloud.dev/pubsub/gcppubsub"
	_ "gocloud.dev/pubsub/natspubsub"
	_ "gocloud.dev/pubsub/rabbitpubsub"
)

type pubCmd struct{}

func (*pubCmd) Name() string     { return "pub" }
func (*pubCmd) Synopsis() string { return "Publish a message to a topic" }
func (*pubCmd) Usage() string {
	return `pub <topic URL>:
  Read messages from stdin, one per line and send them to <topic URL>.

  See https://godoc.org/gocloud.dev#hdr-URLs for more background on
  Go CDK URLs, and sub-packages under gocloud.dev/pubsub
  (https://godoc.org/gocloud.dev/pubsub#pkg-subdirectories)
  for details on the topic URL format.
`
}

func (p *pubCmd) SetFlags(f *flag.FlagSet) {
}

func (p *pubCmd) Execute(ctx context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
	if f.NArg() != 1 {
		f.Usage()
		return subcommands.ExitUsageError
	}
	topicURL := f.Arg(0)
	if err := p.pub(ctx, topicURL, os.Stdin); err != nil {
		log.Print(err)
		return subcommands.ExitFailure
	}
	return subcommands.ExitSuccess
}

func (p *pubCmd) pub(ctx context.Context, topicURL string, r io.Reader) error {
	t, err := pubsub.OpenTopic(ctx, topicURL)
	if err != nil {
		return err
	}

	// Get lines from r and send them as messages to the topic.
	fmt.Fprintf(os.Stderr, "Enter messages, one per line to be published to %q.\n", topicURL)
	scanner := bufio.NewScanner(r)
	for scanner.Scan() {
		line := scanner.Text()
		if line == "" {
			log.Printf("skipping empty message")
			continue
		}
		m := &pubsub.Message{Body: []byte(line)}
		if err := t.Send(ctx, m); err != nil {
			return err
		}
	}
	return scanner.Err()
}

type subCmd struct {
	n int
}

func (*subCmd) Name() string     { return "sub" }
func (*subCmd) Synopsis() string { return "Receive messages from a subscription" }
func (*subCmd) Usage() string {
	return `sub [-n N] <subscription URL>:
  Receive messages from <subscription> and send them to stdout, one per line.

  See https://godoc.org/gocloud.dev#hdr-URLs for more background on
  Go CDK URLs, and sub-packages under gocloud.dev/pubsub
  (https://godoc.org/gocloud.dev/pubsub#pkg-subdirectories)
  for details on the subscription URL format.
`
}

func (s *subCmd) SetFlags(f *flag.FlagSet) {
	f.IntVar(&s.n, "n", 0, "number of messages to receive, or 0 for unlimited")
}

func (s *subCmd) Execute(ctx context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
	if f.NArg() != 1 {
		f.Usage()
		return subcommands.ExitUsageError
	}
	subURL := f.Arg(0)
	if err := s.sub(ctx, subURL, os.Stdout); err != nil {
		log.Println(err)
		return subcommands.ExitFailure
	}
	return subcommands.ExitSuccess
}

func (s *subCmd) sub(ctx context.Context, subURL string, w io.Writer) error {
	sub, err := pubsub.OpenSubscription(ctx, subURL)
	if err != nil {
		return err
	}
	fmt.Fprintf(os.Stderr, "Receiving messages from %q...\n", subURL)

	// Receive messages and send them to w.
	for i := 0; s.n == 0 || i < s.n; i++ {
		m, err := sub.Receive(ctx)
		if err != nil {
			return err
		}
		fmt.Fprintf(w, "%s\n", m.Body)
		m.Ack()
	}
	return nil
}

func main() {
	subcommands.Register(subcommands.HelpCommand(), "")
	subcommands.Register(subcommands.FlagsCommand(), "")
	subcommands.Register(subcommands.CommandsCommand(), "")
	subcommands.Register(&pubCmd{}, "")
	subcommands.Register(&subCmd{}, "")
	log.SetFlags(0)
	log.SetPrefix("gcmsg: ")
	flag.Parse()
	ctx := context.Background()
	os.Exit(int(subcommands.Execute(ctx)))
}
<|endoftext|>


<commit_before>/*
 * Datadog API for Go
 *
 * Please see the included LICENSE file for licensing information.
 *
 * Copyright 2013 by authors and contributors.
 */

package datadog

import (
	"net/url"
	"strconv"
)

// DataPoint is a tuple of [UNIX timestamp, value]. This has to use floats
// because the value could be non-integer.
type DataPoint [2]float64

// Metric represents a collection of data points that we might send or receive
// on one single metric line.
type Metric struct {
	Metric *string     `json:"metric,omitempty"`
	Points []DataPoint `json:"points,omitempty"`
	Type   *string     `json:"type,omitempty"`
	Host   *string     `json:"host,omitempty"`
	Tags   []string    `json:"tags,omitempty"`
	Unit   *string     `json:"unit,omitempty"`
}

// Unit represents a unit definition that we might receive when query for timeseries data.
type Unit struct {
	Family string       `json:"family"`
	ScaleFactor float32 `json:"scale_factor"`
	Name string         `json:"name"`
	ShortName string    `json:"short_name"`
	Plural string       `json:"plural"`
	Id int              `json:"id"`
}

// A Series is characterized by 2 units as: x per y
// One or both could be missing
type UnitPair []*Unit

// Series represents a collection of data points we get when we query for timeseries data
type Series struct {
	Metric      *string     `json:"metric,omitempty"`
	DisplayName *string     `json:"display_name,omitempty"`
	Points      []DataPoint `json:"pointlist,omitempty"`
	Start       *float64    `json:"start,omitempty"`
	End         *float64    `json:"end,omitempty"`
	Interval    *int        `json:"interval,omitempty"`
	Aggr        *string     `json:"aggr,omitempty"`
	Length      *int        `json:"length,omitempty"`
	Scope       *string     `json:"scope,omitempty"`
	Expression  *string     `json:"expression,omitempty"`
	Units       *UnitPair   `json:"unit,omitempty"`
}

// reqPostSeries from /api/v1/series
type reqPostSeries struct {
	Series []Metric `json:"series,omitempty"`
}

// reqMetrics is the container for receiving metric results.
type reqMetrics struct {
	Series []Series `json:"series,omitempty"`
}

// PostMetrics takes as input a slice of metrics and then posts them up to the
// server for posting data.
func (client *Client) PostMetrics(series []Metric) error {
	return client.doJsonRequest("POST", "/v1/series",
		reqPostSeries{Series: series}, nil)
}

// QueryMetrics takes as input from, to (seconds from Unix Epoch) and query string and then requests
// timeseries data for that time peried
func (client *Client) QueryMetrics(from, to int64, query string) ([]Series, error) {
	v := url.Values{}
	v.Add("from", strconv.FormatInt(from, 10))
	v.Add("to", strconv.FormatInt(to, 10))
	v.Add("query", query)

	var out reqMetrics
	err := client.doJsonRequest("GET", "/v1/query?"+v.Encode(), nil, &out)
	if err != nil {
		return nil, err
	}
	return out.Series, nil
}
<commit_msg>Change DataPoint to array of pointers (#146)<commit_after>/*
 * Datadog API for Go
 *
 * Please see the included LICENSE file for licensing information.
 *
 * Copyright 2013 by authors and contributors.
 */

package datadog

import (
	"net/url"
	"strconv"
)

// DataPoint is a tuple of [UNIX timestamp, value]. This has to use floats
// because the value could be non-integer.
type DataPoint [2]*float64

// Metric represents a collection of data points that we might send or receive
// on one single metric line.
type Metric struct {
	Metric *string     `json:"metric,omitempty"`
	Points []DataPoint `json:"points,omitempty"`
	Type   *string     `json:"type,omitempty"`
	Host   *string     `json:"host,omitempty"`
	Tags   []string    `json:"tags,omitempty"`
	Unit   *string     `json:"unit,omitempty"`
}

// Unit represents a unit definition that we might receive when query for timeseries data.
type Unit struct {
	Family string       `json:"family"`
	ScaleFactor float32 `json:"scale_factor"`
	Name string         `json:"name"`
	ShortName string    `json:"short_name"`
	Plural string       `json:"plural"`
	Id int              `json:"id"`
}

// A Series is characterized by 2 units as: x per y
// One or both could be missing
type UnitPair []*Unit

// Series represents a collection of data points we get when we query for timeseries data
type Series struct {
	Metric      *string     `json:"metric,omitempty"`
	DisplayName *string     `json:"display_name,omitempty"`
	Points      []DataPoint `json:"pointlist,omitempty"`
	Start       *float64    `json:"start,omitempty"`
	End         *float64    `json:"end,omitempty"`
	Interval    *int        `json:"interval,omitempty"`
	Aggr        *string     `json:"aggr,omitempty"`
	Length      *int        `json:"length,omitempty"`
	Scope       *string     `json:"scope,omitempty"`
	Expression  *string     `json:"expression,omitempty"`
	Units       *UnitPair   `json:"unit,omitempty"`
}

// reqPostSeries from /api/v1/series
type reqPostSeries struct {
	Series []Metric `json:"series,omitempty"`
}

// reqMetrics is the container for receiving metric results.
type reqMetrics struct {
	Series []Series `json:"series,omitempty"`
}

// PostMetrics takes as input a slice of metrics and then posts them up to the
// server for posting data.
func (client *Client) PostMetrics(series []Metric) error {
	return client.doJsonRequest("POST", "/v1/series",
		reqPostSeries{Series: series}, nil)
}

// QueryMetrics takes as input from, to (seconds from Unix Epoch) and query string and then requests
// timeseries data for that time peried
func (client *Client) QueryMetrics(from, to int64, query string) ([]Series, error) {
	v := url.Values{}
	v.Add("from", strconv.FormatInt(from, 10))
	v.Add("to", strconv.FormatInt(to, 10))
	v.Add("query", query)

	var out reqMetrics
	err := client.doJsonRequest("GET", "/v1/query?"+v.Encode(), nil, &out)
	if err != nil {
		return nil, err
	}
	return out.Series, nil
}
<|endoftext|>


<commit_before>package gocd

import (
	"context"
	"errors"
	"fmt"
	"github.com/google/go-github/v44/github"
	"github.com/servehub/serve/manifest"
	"golang.org/x/oauth2"
	"os"
	"strings"
)

func init() {
	manifest.PluginRegestry.Add("github.status", githubStatus{})
}

type githubStatus struct{}

func (p githubStatus) Run(data manifest.Manifest) error {
	accessToken := os.Getenv("GITHUB_TOKEN")
	if accessToken == "" {
		return errors.New("`GITHUB_TOKEN` is required")
	}

	client := github.NewClient(
		oauth2.NewClient(
			context.Background(),
			oauth2.StaticTokenSource(&oauth2.Token{AccessToken: accessToken})))

	rp := strings.SplitN(data.GetString("repo"), ":", 2)
	rps := strings.SplitN(rp[1], "/", 2)

	state := data.GetStringOr("state", "success")
	if !IsValidState(state) {
		return fmt.Errorf("`%s` is not a valid value for a state", state)
	}

	input := &github.RepoStatus{
		State:       github.String(state),
		TargetURL:   github.String(data.GetString("target-url")),
		Description: github.String(data.GetStringOr("description", fmt.Sprintf("The build status:  %s", state))),
		Context:     github.String(data.GetStringOr("context", "continuous-integration/serve")),
	}

	_, _, err := client.Repositories.CreateStatus(context.Background(), rps[0], strings.TrimSuffix(rps[1], ".git"), data.GetString("ref"), input)
	return err
}

func IsValidState(state string) bool {
	switch state {
	case "error", "failure", "pending", "success":
		return true
	}
	return false
}
<commit_msg>github.status: retry<commit_after>package gocd

import (
	"context"
	"errors"
	"fmt"
	"github.com/cenk/backoff"
	"github.com/google/go-github/v44/github"
	"github.com/servehub/serve/manifest"
	"golang.org/x/oauth2"
	"os"
	"strings"
)

func init() {
	manifest.PluginRegestry.Add("github.status", githubStatus{})
}

type githubStatus struct{}

func (p githubStatus) Run(data manifest.Manifest) error {
	accessToken := os.Getenv("GITHUB_TOKEN")
	if accessToken == "" {
		return errors.New("`GITHUB_TOKEN` is required")
	}

	client := github.NewClient(
		oauth2.NewClient(
			context.Background(),
			oauth2.StaticTokenSource(&oauth2.Token{AccessToken: accessToken})))

	rp := strings.SplitN(data.GetString("repo"), ":", 2)
	rps := strings.SplitN(rp[1], "/", 2)

	state := data.GetStringOr("state", "success")
	if !IsValidState(state) {
		return fmt.Errorf("`%s` is not a valid value for a state", state)
	}

	input := &github.RepoStatus{
		State:       github.String(state),
		TargetURL:   github.String(data.GetString("target-url")),
		Description: github.String(data.GetStringOr("description", fmt.Sprintf("The build status:  %s", state))),
		Context:     github.String(data.GetStringOr("context", "continuous-integration/serve")),
	}

	return backoff.Retry(func() error {
		_, _, err := client.Repositories.CreateStatus(context.Background(), rps[0], strings.TrimSuffix(rps[1], ".git"), data.GetString("ref"), input)
		return err
	}, backoff.NewExponentialBackOff())
}

func IsValidState(state string) bool {
	switch state {
	case "error", "failure", "pending", "success":
		return true
	}
	return false
}
<|endoftext|>


<commit_before>package gracehttp

import (
	"crypto/tls"
	"fmt"
	"log"
	"net"
	"net/http"
	"os"
	"os/signal"
	"syscall"
	"time"
)

const (
	GRACEFUL_ENVIRON_KEY    = "IS_GRACEFUL"
	GRACEFUL_ENVIRON_STRING = GRACEFUL_ENVIRON_KEY + "=1"

	DEFAULT_READ_TIMEOUT  = 30 * time.Second
	DEFAULT_WRITE_TIMEOUT = DEFAULT_READ_TIMEOUT
)

// refer http.ListenAndServe
func ListenAndServe(addr string, handler http.Handler) error {
	return NewServer(addr, handler, DEFAULT_READ_TIMEOUT, DEFAULT_WRITE_TIMEOUT).ListenAndServe()
}

// refer http.ListenAndServeTLS
func ListenAndServeTLS(addr string, certFile string, keyFile string, handler http.Handler) error {
	return NewServer(addr, handler, DEFAULT_READ_TIMEOUT, DEFAULT_WRITE_TIMEOUT).ListenAndServeTLS(certFile, keyFile)
}

// new server
func NewServer(addr string, handler http.Handler, readTimeout, writeTimeout time.Duration) *Server {

	// 获取环境变量
	isGraceful := false
	gracefulEnvironValue := os.Getenv(GRACEFUL_ENVIRON_KEY)
	if gracefulEnvironValue != "" {
		isGraceful = true
	}

	// 实例化Server
	return &Server{
		httpServer: &http.Server{
			Addr:    addr,
			Handler: handler,

			ReadTimeout:  readTimeout,
			WriteTimeout: writeTimeout,
		},

		isGraceful: isGraceful,
		signalChan: make(chan os.Signal),
	}
}

// 支持优雅重启的http服务
type Server struct {
	httpServer *http.Server
	listener   net.Listener

	isGraceful bool
	signalChan chan os.Signal
}

func (this *Server) ListenAndServe() error {
	addr := this.httpServer.Addr
	if addr == "" {
		addr = ":http"
	}

	ln, err := this.getNetListener(addr)
	if err != nil {
		return err
	}

	this.listener = newListener(ln.(*net.TCPListener))

	return this.Serve()
}

func (this *Server) ListenAndServeTLS(certFile, keyFile string) error {
	addr := this.httpServer.Addr
	if addr == "" {
		addr = ":https"
	}

	config := &tls.Config{}
	if this.httpServer.TLSConfig != nil {
		*config = *this.httpServer.TLSConfig
	}
	if config.NextProtos == nil {
		config.NextProtos = []string{"http/1.1"}
	}

	var err error
	config.Certificates = make([]tls.Certificate, 1)
	config.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile)
	if err != nil {
		return err
	}

	ln, err := this.getNetListener(addr)
	if err != nil {
		return err
	}

	this.listener = tls.NewListener(newListener(ln.(*net.TCPListener)), config)
	return this.Serve()
}

func (this *Server) Serve() error {

	// 处理信号
	go this.handleSignals()

	// 处理HTTP请求
	err := this.httpServer.Serve(this.listener)

	// 跳出Serve处理代表 listener 已经close，等待所有已有的连接处理结束
	this.logf("listener closed, waiting for connection close...")
	this.listener.(*Listener).Wait()

	return err
}

func (this *Server) getNetListener(addr string) (ln net.Listener, err error) {

	if this.isGraceful {
		file := os.NewFile(3, "")
		ln, err = net.FileListener(file)
		if err != nil {
			err = fmt.Errorf("net.FileListener error: %v", err)
			return
		}
	} else {
		ln, err = net.Listen("tcp", addr)
		if err != nil {
			err = fmt.Errorf("net.Listen error: %v", err)
			return
		}
	}
	return
}

func (this *Server) handleSignals() {
	var sig os.Signal

	signal.Notify(
		this.signalChan,
		syscall.SIGHUP,
		syscall.SIGTERM,
	)

	pid := os.Getpid()
	for {
		sig = <-this.signalChan

		switch sig {

		case syscall.SIGTERM:

			this.logf("pid %d received SIGTERM.", pid)
			this.logf("graceful shutting down http server...")
			this.shutdown()

		case syscall.SIGHUP:

			this.logf("pid %d received SIGHUP.", pid)
			this.logf("graceful restart http server...")

			err := this.fork()
			if err != nil {
				this.logf("fork error: %v.", err)
			} else {
				this.shutdown()
			}

		default:

		}
	}
}

// 优雅关闭
func (this *Server) shutdown() {

	// 通过设置超时使得进程不再接受新请求
	this.listener.(*Listener).SetDeadline(time.Now())

	// 关闭链接
	this.listener.(*Listener).Close()
}

// 启动子进程执行新程序
func (this *Server) fork() error {

	listenerFd, err := this.listener.(*Listener).GetFd()
	if err != nil {
		return fmt.Errorf("failed to get socket file descriptor: %v.", err)
	}

	path := os.Args[0]

	// 设置标识优雅重启的环境变量
	environList := []string{}
	for _, value := range os.Environ() {
		if value != GRACEFUL_ENVIRON_STRING {
			environList = append(environList, value)
		}
	}
	environList = append(environList, GRACEFUL_ENVIRON_STRING)

	execSpec := &syscall.ProcAttr{
		Env:   environList,
		Files: []uintptr{os.Stdin.Fd(), os.Stdout.Fd(), os.Stderr.Fd(), listenerFd},
	}

	fork, err := syscall.ForkExec(path, os.Args, execSpec)
	if err != nil {
		return fmt.Errorf("failed to forkexec: %v.", err)
	}

	this.logf("fork exec to pid %d.", fork)

	return nil
}

func (this *Server) logf(format string, args ...interface{}) {

	if this.httpServer.ErrorLog != nil {
		this.httpServer.ErrorLog.Printf(format, args...)
	} else {
		log.Printf(format, args...)
	}
}
<commit_msg>clear code<commit_after>package gracehttp

import (
	"crypto/tls"
	"fmt"
	"log"
	"net"
	"net/http"
	"os"
	"os/signal"
	"syscall"
	"time"
)

const (
	GRACEFUL_ENVIRON_KEY    = "IS_GRACEFUL"
	GRACEFUL_ENVIRON_STRING = GRACEFUL_ENVIRON_KEY + "=1"

	DEFAULT_READ_TIMEOUT  = 30 * time.Second
	DEFAULT_WRITE_TIMEOUT = DEFAULT_READ_TIMEOUT
)

// refer http.ListenAndServe
func ListenAndServe(addr string, handler http.Handler) error {
	return NewServer(addr, handler, DEFAULT_READ_TIMEOUT, DEFAULT_WRITE_TIMEOUT).ListenAndServe()
}

// refer http.ListenAndServeTLS
func ListenAndServeTLS(addr string, certFile string, keyFile string, handler http.Handler) error {
	return NewServer(addr, handler, DEFAULT_READ_TIMEOUT, DEFAULT_WRITE_TIMEOUT).ListenAndServeTLS(certFile, keyFile)
}

// new server
func NewServer(addr string, handler http.Handler, readTimeout, writeTimeout time.Duration) *Server {

	// 获取环境变量
	isGraceful := false
	if os.Getenv(GRACEFUL_ENVIRON_KEY) != "" {
		isGraceful = true
	}

	// 实例化Server
	return &Server{
		httpServer: &http.Server{
			Addr:    addr,
			Handler: handler,

			ReadTimeout:  readTimeout,
			WriteTimeout: writeTimeout,
		},

		isGraceful: isGraceful,
		signalChan: make(chan os.Signal),
	}
}

// 支持优雅重启的http服务
type Server struct {
	httpServer *http.Server
	listener   net.Listener

	isGraceful bool
	signalChan chan os.Signal
}

func (this *Server) ListenAndServe() error {
	addr := this.httpServer.Addr
	if addr == "" {
		addr = ":http"
	}

	ln, err := this.getNetListener(addr)
	if err != nil {
		return err
	}

	this.listener = newListener(ln.(*net.TCPListener))

	return this.Serve()
}

func (this *Server) ListenAndServeTLS(certFile, keyFile string) error {
	addr := this.httpServer.Addr
	if addr == "" {
		addr = ":https"
	}

	config := &tls.Config{}
	if this.httpServer.TLSConfig != nil {
		*config = *this.httpServer.TLSConfig
	}
	if config.NextProtos == nil {
		config.NextProtos = []string{"http/1.1"}
	}

	var err error
	config.Certificates = make([]tls.Certificate, 1)
	config.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile)
	if err != nil {
		return err
	}

	ln, err := this.getNetListener(addr)
	if err != nil {
		return err
	}

	this.listener = tls.NewListener(newListener(ln.(*net.TCPListener)), config)
	return this.Serve()
}

func (this *Server) Serve() error {

	// 处理信号
	go this.handleSignals()

	// 处理HTTP请求
	err := this.httpServer.Serve(this.listener)

	// 跳出Serve处理代表 listener 已经close，等待所有已有的连接处理结束
	this.logf("listener closed, waiting for connection close...")
	this.listener.(*Listener).Wait()

	return err
}

func (this *Server) getNetListener(addr string) (ln net.Listener, err error) {

	if this.isGraceful {
		file := os.NewFile(3, "")
		ln, err = net.FileListener(file)
		if err != nil {
			err = fmt.Errorf("net.FileListener error: %v", err)
			return
		}
	} else {
		ln, err = net.Listen("tcp", addr)
		if err != nil {
			err = fmt.Errorf("net.Listen error: %v", err)
			return
		}
	}
	return
}

func (this *Server) handleSignals() {
	var sig os.Signal

	signal.Notify(
		this.signalChan,
		syscall.SIGHUP,
		syscall.SIGTERM,
	)

	pid := os.Getpid()
	for {
		sig = <-this.signalChan

		switch sig {

		case syscall.SIGTERM:

			this.logf("pid %d received SIGTERM.", pid)
			this.logf("graceful shutting down http server...")
			this.shutdown()

		case syscall.SIGHUP:

			this.logf("pid %d received SIGHUP.", pid)
			this.logf("graceful restart http server...")

			err := this.fork()
			if err != nil {
				this.logf("fork error: %v.", err)
			} else {
				this.shutdown()
			}

		default:

		}
	}
}

// 优雅关闭
func (this *Server) shutdown() {

	// 通过设置超时使得进程不再接受新请求
	this.listener.(*Listener).SetDeadline(time.Now())

	// 关闭链接
	this.listener.(*Listener).Close()
}

// 启动子进程执行新程序
func (this *Server) fork() error {

	listenerFd, err := this.listener.(*Listener).GetFd()
	if err != nil {
		return fmt.Errorf("failed to get socket file descriptor: %v.", err)
	}

	path := os.Args[0]

	// 设置标识优雅重启的环境变量
	environList := []string{}
	for _, value := range os.Environ() {
		if value != GRACEFUL_ENVIRON_STRING {
			environList = append(environList, value)
		}
	}
	environList = append(environList, GRACEFUL_ENVIRON_STRING)

	execSpec := &syscall.ProcAttr{
		Env:   environList,
		Files: []uintptr{os.Stdin.Fd(), os.Stdout.Fd(), os.Stderr.Fd(), listenerFd},
	}

	fork, err := syscall.ForkExec(path, os.Args, execSpec)
	if err != nil {
		return fmt.Errorf("failed to forkexec: %v.", err)
	}

	this.logf("fork exec to pid %d.", fork)

	return nil
}

func (this *Server) logf(format string, args ...interface{}) {

	if this.httpServer.ErrorLog != nil {
		this.httpServer.ErrorLog.Printf(format, args...)
	} else {
		log.Printf(format, args...)
	}
}
<|endoftext|>


<commit_before>package sdproxy

import (
	"log"
	"net/http"
	"strings"
)

type Server struct {
	locations []*Location
}

// FIFO you are in charge of how it will match
func (s *Server) AddLocation(locations ...*Location) {
	if len(locations) == 0 {
		return
	}
	s.locations = append(s.locations, locations...)
}

func (s *Server) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	for _, location := range s.locations {
		log.Println(location.path, req.URL.RequestURI())
		if strings.HasPrefix(req.URL.RequestURI(), location.path) {
			location.Serve(rw, req)
			return
		}
	}
	http.Error(rw, http.StatusText(http.StatusNotFound), http.StatusNotFound)
}

func (s *Server) ListenAndServe(addr string) error {
	return http.ListenAndServe(addr, s)
}

func NewServer(locations ...*Location) *Server {
	server := &Server{}
	server.AddLocation(locations...)
	return server
}
<commit_msg>Remove log printing<commit_after>package sdproxy

import (
	"net/http"
	"strings"
)

type Server struct {
	locations []*Location
}

// FIFO you are in charge of how it will match
func (s *Server) AddLocation(locations ...*Location) {
	if len(locations) == 0 {
		return
	}
	s.locations = append(s.locations, locations...)
}

func (s *Server) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	for _, location := range s.locations {
		if strings.HasPrefix(req.URL.RequestURI(), location.path) {
			location.Serve(rw, req)
			return
		}
	}
	http.Error(rw, http.StatusText(http.StatusNotFound), http.StatusNotFound)
}

func (s *Server) ListenAndServe(addr string) error {
	return http.ListenAndServe(addr, s)
}

func NewServer(locations ...*Location) *Server {
	server := &Server{}
	server.AddLocation(locations...)
	return server
}
<|endoftext|>


<commit_before>package main

import (
	"bitbucket.org/bertimus9/systemstat"
	"flag"
	"fmt"
	"github.com/codegangsta/martini-contrib/render"
	"github.com/fsouza/go-dockerclient"
	"github.com/go-martini/martini"
	"net/http"
	"strconv"
)

type MonitorSample struct {
	Name     string  `json:"name"`
	CpuPct   float64 `json:"cpu_percent,omitempty"`
	MemUsed  uint64  `json:"mem_used"`
	MemTotal uint64  `json:"mem_total"`
}

func getSystemMonitorSample() MonitorSample {
	cpuSample := systemstat.GetCPUSample()
	idle := float64(cpuSample.Idle)
	total := float64(cpuSample.Total)

	memSample := systemstat.GetMemSample()

	var stat MonitorSample
	stat.Name = "system"
	stat.CpuPct = (total - idle) * 100.0 / total
	stat.MemTotal = memSample.MemTotal
	stat.MemUsed = memSample.MemUsed
	return stat
}

func getContainerMonitorSample(name string) (MonitorSample, error) {
	var stat MonitorSample
	dockerStats := make(chan *docker.Stats)
	errC := make(chan error, 1)
	go func() {
		errC <- client.Stats(docker.StatsOptions{name, dockerStats, false, false})
		close(errC)
	}()

	statsResult, ok := <-dockerStats
	if !ok {
		return stat, <-errC
	}
	stat.Name = name
	stat.MemTotal = statsResult.MemoryStats.Limit
	stat.MemUsed = statsResult.MemoryStats.Usage
	return stat, nil
}

var client *docker.Client

func main() {
	var port int
	flag.IntVar(&port, "port", 3001, "server port")
	flag.Parse()
	fmt.Println("Port: ", port)

	endpoint := "unix:///var/run/docker.sock"
	var err error
	client, err = docker.NewClient(endpoint)
	if err != nil {
		panic(err)
	}

	m := martini.Classic()
	m.Use(render.Renderer())

	m.Get("/", func(r render.Render) {
		r.JSON(http.StatusOK, getSystemMonitorSample())
	})

	m.Get("/:name", func(args martini.Params, r render.Render) {
		name := args["name"]
		sample, err := getContainerMonitorSample(name)
		if err == nil {
			r.JSON(http.StatusOK, sample)
		} else {
			r.JSON(http.StatusBadRequest, err)
		}
	})

	http.Handle("/", m)
	http.ListenAndServe(":"+strconv.Itoa(port), nil)
}
<commit_msg>Rethinking and now actually fixing it.<commit_after>package main

import (
	"bitbucket.org/bertimus9/systemstat"
	"flag"
	"fmt"
	"github.com/codegangsta/martini-contrib/render"
	"github.com/fsouza/go-dockerclient"
	"github.com/go-martini/martini"
	"net/http"
	"strconv"
)

type MonitorSample struct {
	Name     string  `json:"name"`
	CpuPct   float64 `json:"cpu_percent,omitempty"`
	MemUsed  uint64  `json:"mem_used"`
	MemTotal uint64  `json:"mem_total"`
}

func getSystemMonitorSample() MonitorSample {
	cpuSample := systemstat.GetCPUSample()
	idle := float64(cpuSample.Idle)
	total := float64(cpuSample.Total)

	memSample := systemstat.GetMemSample()

	var stat MonitorSample
	stat.Name = "system"
	stat.CpuPct = (total - idle) * 100.0 / total
	stat.MemTotal = memSample.MemTotal
	stat.MemUsed = memSample.MemUsed
	return stat
}

func getContainerMonitorSample(name string) (MonitorSample, error) {
	var stat MonitorSample
	dockerStats := make(chan *docker.Stats)
	errC := make(chan error, 1)
	stopOperation := make(chan *bool)
	go func() {
		errC <- client.Stats(docker.StatsOptions{name, dockerStats, false, stopOperation})
		close(errC)
	}()

	statsResult, ok := <-dockerStats
	if !ok {
		return stat, <-errC
	}
	stat.Name = name
	stat.MemTotal = statsResult.MemoryStats.Limit
	stat.MemUsed = statsResult.MemoryStats.Usage
	return stat, nil
}

var client *docker.Client

func main() {
	var port int
	flag.IntVar(&port, "port", 3001, "server port")
	flag.Parse()
	fmt.Println("Port: ", port)

	endpoint := "unix:///var/run/docker.sock"
	var err error
	client, err = docker.NewClient(endpoint)
	if err != nil {
		panic(err)
	}

	m := martini.Classic()
	m.Use(render.Renderer())

	m.Get("/", func(r render.Render) {
		r.JSON(http.StatusOK, getSystemMonitorSample())
	})

	m.Get("/:name", func(args martini.Params, r render.Render) {
		name := args["name"]
		sample, err := getContainerMonitorSample(name)
		if err == nil {
			r.JSON(http.StatusOK, sample)
		} else {
			r.JSON(http.StatusBadRequest, err)
		}
	})

	http.Handle("/", m)
	http.ListenAndServe(":"+strconv.Itoa(port), nil)
}
<|endoftext|>


<commit_before>package main

import (
    "net/http"
    "log"
    "encoding/json"
    "crypto/md5"
    "time"
    "io"
    "fmt"
    "sync"

    "code.google.com/p/go.net/websocket"
)

type Server struct {
    ID      string
    Config  *Configuration
    Store   *Storage
}

func createServer(conf *Configuration, store *Storage) *Server{
    hash := md5.New()
    io.WriteString(hash, time.Now().String())
    id := string(hash.Sum(nil))

    return &Server{id, conf, store}
}

func (this *Server) initSocketListener() {
    Connect := func(ws *websocket.Conn) {
        sock := newSocket(ws, this, "")

        if DEBUG { log.Printf("Socket connected via %s\n", ws.RemoteAddr()) }
        if err := sock.Authenticate(); err != nil {
            if DEBUG { log.Printf("Error: %s\n", err.Error()) }
            return
        }

        var wg sync.WaitGroup
        wg.Add(2)

        go sock.listenForMessages(&wg)
        go sock.listenForWrites(&wg)

        wg.Wait()
        if DEBUG { log.Println("Socket Closed") }
    }

    http.Handle("/socket", websocket.Handler(Connect))
}

func (this *Server) initAppListener() {
    rec := make(chan []string)

    consumer, err := this.Store.redis.Subscribe(rec, "Message")
    if err != nil {
        log.Fatal("Couldn't subscribe to redis channel")
    }
    defer consumer.Quit()

    if DEBUG { log.Println("LISENING FOR REDIS MESSAGE") }
    var ms []string
    for {
        var cmd CommandMsg
        ms = <- rec
        json.Unmarshal([]byte(ms[2]), &cmd)
        go cmd.FromRedis(this)

        if DEBUG { log.Printf("Received %v\n", cmd.Command) }
    }
}

func (this *Server) initPingListener() {
    pingHandler := func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprint(w, "OK")
    }

    http.HandleFunc("/ping", pingHandler)
}<commit_msg>removed unneeded debug line<commit_after>package main

import (
    "net/http"
    "log"
    "encoding/json"
    "crypto/md5"
    "time"
    "io"
    "fmt"
    "sync"

    "code.google.com/p/go.net/websocket"
)

type Server struct {
    ID      string
    Config  *Configuration
    Store   *Storage
}

func createServer(conf *Configuration, store *Storage) *Server{
    hash := md5.New()
    io.WriteString(hash, time.Now().String())
    id := string(hash.Sum(nil))

    return &Server{id, conf, store}
}

func (this *Server) initSocketListener() {
    Connect := func(ws *websocket.Conn) {
        sock := newSocket(ws, this, "")

        if DEBUG { log.Printf("Socket connected via %s\n", ws.RemoteAddr()) }
        if err := sock.Authenticate(); err != nil {
            if DEBUG { log.Printf("Error: %s\n", err.Error()) }
            return
        }

        var wg sync.WaitGroup
        wg.Add(2)

        go sock.listenForMessages(&wg)
        go sock.listenForWrites(&wg)

        wg.Wait()
        if DEBUG { log.Println("Socket Closed") }
    }

    http.Handle("/socket", websocket.Handler(Connect))
}

func (this *Server) initAppListener() {
    rec := make(chan []string)

    consumer, err := this.Store.redis.Subscribe(rec, "Message")
    if err != nil {
        log.Fatal("Couldn't subscribe to redis channel")
    }
    defer consumer.Quit()

    if DEBUG { log.Println("LISENING FOR REDIS MESSAGE") }
    var ms []string
    for {
        var cmd CommandMsg
        ms = <- rec
        json.Unmarshal([]byte(ms[2]), &cmd)
        go cmd.FromRedis(this)
    }
}

func (this *Server) initPingListener() {
    pingHandler := func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprint(w, "OK")
    }

    http.HandleFunc("/ping", pingHandler)
}<|endoftext|>


<commit_before>package main

import (
	urlexpander "./lib"
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"strconv"
	"syscall"
	"time"
)

// parse flags from cmd-line
func parseArguments() (port int, userAgent string, cacheCapacity int, cacheExpiration time.Duration, apiOnly bool) {
	flag.BoolVar(&apiOnly, "api-only", false, "Expose only the API end points")
	flag.IntVar(&port, "port", 8080, "Bind webserver to given port.")
	flag.StringVar(&userAgent, "user-agent",
		"Mozilla/5.0 (compatible; UrlExpander/1.0)",
		" User agent string used when translating shortened url.")
	flag.IntVar(&cacheCapacity, "cache-capacity",
		100000,
		"Expanded urls are cached for repeated queries. Using this option cache capacity can be set.")

	var cacheExpirationMinutes int
	flag.IntVar(&cacheExpirationMinutes, "cache-expiration",
		60,
		"Set cache expiration time in minutes.")

	help := flag.Bool("help", false, "Print usage.")
	flag.Parse()

	cacheExpiration = time.Duration(cacheExpirationMinutes) * time.Minute

	// print usage
	if *help {
		fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
		flag.PrintDefaults()
		syscall.Exit(0)
	}

	return
}

type App struct {
	Expander urlexpander.UrlExpander
}

func main() {
	port, userAgent, cacheCapacity, cacheExpiration, apiOnly := parseArguments()

	conf := urlexpander.Config{
		CacheCapacity:     cacheCapacity,
		CacheExpiration:   cacheExpiration,
		UserAgent:         userAgent,
		ShortUrlMaxLength: 32,
	}

	log.Printf("INFO: Used configuration: %#v", conf)

	app := App{Expander: urlexpander.NewFromConfig(conf)}

	log.Printf("INFO: Listening on port %d", port)
	if !apiOnly {
		http.Handle("/", http.FileServer(http.Dir("./static")))
	}
	http.HandleFunc("/api/expand", app.expand)
	err := http.ListenAndServe(":"+strconv.Itoa(port), nil)
	if err != nil {
		panic(err)
	}
}

type responseMessage struct {
	Original string `json:"original,omitempty"`
	Expanded string `json:"expanded,omitempty"`
	Error    string `json:"error,omitempty"`
}

// Handle /expand API endpoint calls
func (app *App) expand(w http.ResponseWriter, r *http.Request) {
	shortened := r.URL.Query().Get("url")
	if shortened == "" {
		resp, _ := json.Marshal(responseMessage{Error: "No url provided. Use 'url' param."})
		http.Error(w, string(resp), http.StatusBadRequest)
		return
	}

	expanded, err := app.Expander.ExpandUrl(shortened)
	if err != nil {
		log.Printf("ERROR: (%s) %s [url: %s]", r.RemoteAddr, err.Error(), shortened)
		switch err {
		case urlexpander.ErrDisallowedByRobotsTxt, urlexpander.ErrInvalidUrl, urlexpander.ErrLongUrl:
			resp, _ := json.Marshal(responseMessage{Error: err.Error()})
			http.Error(w, string(resp), http.StatusBadRequest)
		default:
			resp, _ := json.Marshal(responseMessage{Error: "Uknown error"})
			http.Error(w, string(resp), http.StatusInternalServerError)
		}
		return
	}

	resp := responseMessage{Original: shortened, Expanded: expanded}
	json.NewEncoder(w).Encode(resp)
}
<commit_msg>#! Revert back to absolute import<commit_after>package main

import (
	urlexpander "github.com/vanekjar/urlexpander/lib"
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"strconv"
	"syscall"
	"time"
)

// parse flags from cmd-line
func parseArguments() (port int, userAgent string, cacheCapacity int, cacheExpiration time.Duration, apiOnly bool) {
	flag.BoolVar(&apiOnly, "api-only", false, "Expose only the API end points")
	flag.IntVar(&port, "port", 8080, "Bind webserver to given port.")
	flag.StringVar(&userAgent, "user-agent",
		"Mozilla/5.0 (compatible; UrlExpander/1.0)",
		" User agent string used when translating shortened url.")
	flag.IntVar(&cacheCapacity, "cache-capacity",
		100000,
		"Expanded urls are cached for repeated queries. Using this option cache capacity can be set.")

	var cacheExpirationMinutes int
	flag.IntVar(&cacheExpirationMinutes, "cache-expiration",
		60,
		"Set cache expiration time in minutes.")

	help := flag.Bool("help", false, "Print usage.")
	flag.Parse()

	cacheExpiration = time.Duration(cacheExpirationMinutes) * time.Minute

	// print usage
	if *help {
		fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
		flag.PrintDefaults()
		syscall.Exit(0)
	}

	return
}

type App struct {
	Expander urlexpander.UrlExpander
}

func main() {
	port, userAgent, cacheCapacity, cacheExpiration, apiOnly := parseArguments()

	conf := urlexpander.Config{
		CacheCapacity:     cacheCapacity,
		CacheExpiration:   cacheExpiration,
		UserAgent:         userAgent,
		ShortUrlMaxLength: 32,
	}

	log.Printf("INFO: Used configuration: %#v", conf)

	app := App{Expander: urlexpander.NewFromConfig(conf)}

	log.Printf("INFO: Listening on port %d", port)
	if !apiOnly {
		http.Handle("/", http.FileServer(http.Dir("./static")))
	}
	http.HandleFunc("/api/expand", app.expand)
	err := http.ListenAndServe(":"+strconv.Itoa(port), nil)
	if err != nil {
		panic(err)
	}
}

type responseMessage struct {
	Original string `json:"original,omitempty"`
	Expanded string `json:"expanded,omitempty"`
	Error    string `json:"error,omitempty"`
}

// Handle /expand API endpoint calls
func (app *App) expand(w http.ResponseWriter, r *http.Request) {
	shortened := r.URL.Query().Get("url")
	if shortened == "" {
		resp, _ := json.Marshal(responseMessage{Error: "No url provided. Use 'url' param."})
		http.Error(w, string(resp), http.StatusBadRequest)
		return
	}

	expanded, err := app.Expander.ExpandUrl(shortened)
	if err != nil {
		log.Printf("ERROR: (%s) %s [url: %s]", r.RemoteAddr, err.Error(), shortened)
		switch err {
		case urlexpander.ErrDisallowedByRobotsTxt, urlexpander.ErrInvalidUrl, urlexpander.ErrLongUrl:
			resp, _ := json.Marshal(responseMessage{Error: err.Error()})
			http.Error(w, string(resp), http.StatusBadRequest)
		default:
			resp, _ := json.Marshal(responseMessage{Error: "Uknown error"})
			http.Error(w, string(resp), http.StatusInternalServerError)
		}
		return
	}

	resp := responseMessage{Original: shortened, Expanded: expanded}
	json.NewEncoder(w).Encode(resp)
}
<|endoftext|>