<commit_before>// Copyright (c) 2016 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package storage

import (
	"errors"
	"fmt"
	"sync"
	"time"

	"github.com/m3db/m3/src/dbnode/clock"
	"github.com/m3db/m3/src/dbnode/storage/bootstrap"
	xerrors "github.com/m3db/m3/src/x/errors"
	"github.com/m3db/m3/src/x/instrument"

	"github.com/uber-go/tally"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
)

var (
	// errNamespaceIsBootstrapping raised when trying to bootstrap a namespace that's being bootstrapped.
	errNamespaceIsBootstrapping = errors.New("namespace is bootstrapping")

	// errNamespaceNotBootstrapped raised when trying to flush/snapshot data for a namespace that's not yet bootstrapped.
	errNamespaceNotBootstrapped = errors.New("namespace is not yet bootstrapped")

	// errShardIsBootstrapping raised when trying to bootstrap a shard that's being bootstrapped.
	errShardIsBootstrapping = errors.New("shard is bootstrapping")

	// errShardNotBootstrappedToFlush raised when trying to flush data for a shard that's not yet bootstrapped.
	errShardNotBootstrappedToFlush = errors.New("shard is not yet bootstrapped to flush")

	// errShardNotBootstrappedToSnapshot raised when trying to snapshot data for a shard that's not yet bootstrapped.
	errShardNotBootstrappedToSnapshot = errors.New("shard is not yet bootstrapped to snapshot")

	// errShardNotBootstrappedToRead raised when trying to read data for a shard that's not yet bootstrapped.
	errShardNotBootstrappedToRead = errors.New("shard is not yet bootstrapped to read")

	// errIndexNotBootstrappedToRead raised when trying to read the index before being bootstrapped.
	errIndexNotBootstrappedToRead = errors.New("index is not yet bootstrapped to read")

	// errBootstrapEnqueued raised when trying to bootstrap and bootstrap becomes enqueued.
	errBootstrapEnqueued = errors.New("database bootstrapping enqueued bootstrap")
)

const (
	bootstrapRetryInterval = 10 * time.Second
)

type bootstrapFn func() error

type bootstrapManager struct {
	sync.RWMutex

	database                    database
	mediator                    databaseMediator
	opts                        Options
	log                         *zap.Logger
	bootstrapFn                 bootstrapFn
	nowFn                       clock.NowFn
	sleepFn                     sleepFn
	processProvider             bootstrap.ProcessProvider
	state                       BootstrapState
	hasPending                  bool
	status                      tally.Gauge
	bootstrapDuration           tally.Timer
	lastBootstrapCompletionTime time.Time
}

func newBootstrapManager(
	database database,
	mediator databaseMediator,
	opts Options,
) databaseBootstrapManager {
	scope := opts.InstrumentOptions().MetricsScope()
	m := &bootstrapManager{
		database:          database,
		mediator:          mediator,
		opts:              opts,
		log:               opts.InstrumentOptions().Logger(),
		nowFn:             opts.ClockOptions().NowFn(),
		sleepFn:           time.Sleep,
		processProvider:   opts.BootstrapProcessProvider(),
		status:            scope.Gauge("bootstrapped"),
		bootstrapDuration: scope.Timer("bootstrap-duration"),
	}
	m.bootstrapFn = m.bootstrap
	return m
}

func (m *bootstrapManager) IsBootstrapped() bool {
	m.RLock()
	state := m.state
	m.RUnlock()
	return state == Bootstrapped
}

func (m *bootstrapManager) LastBootstrapCompletionTime() (time.Time, bool) {
	return m.lastBootstrapCompletionTime, !m.lastBootstrapCompletionTime.IsZero()
}

func (m *bootstrapManager) Bootstrap() (BootstrapResult, error) {
	m.Lock()
	switch m.state {
	case Bootstrapping:
		// NB(r): Already bootstrapping, now a consequent bootstrap
		// request comes in - we queue this up to bootstrap again
		// once the current bootstrap has completed.
		// This is an edge case that can occur if during either an
		// initial bootstrap or a resharding bootstrap if a new
		// reshard occurs and we need to bootstrap more shards.
		m.hasPending = true
		m.Unlock()
		return BootstrapResult{AlreadyBootstrapping: true}, errBootstrapEnqueued
	default:
		m.state = Bootstrapping
	}
	m.Unlock()

	// NB(xichen): disable filesystem manager before we bootstrap to minimize
	// the impact of file operations on bootstrapping performance
	m.mediator.DisableFileOps()
	defer m.mediator.EnableFileOps()

	// Keep performing bootstraps until none pending and no error returned.
	var result BootstrapResult
	for i := 0; true; i++ {
		// NB(r): Decouple implementation of bootstrap so can override in tests.
		bootstrapErr := m.bootstrapFn()
		if bootstrapErr != nil {
			result.ErrorsBootstrap = append(result.ErrorsBootstrap, bootstrapErr)
		}

		m.Lock()
		currPending := m.hasPending
		if currPending {
			// New bootstrap calls should now enqueue another pending bootstrap
			m.hasPending = false
		} else {
			m.state = Bootstrapped
		}
		m.Unlock()

		if currPending {
			// NB(r): Requires another bootstrap.
			continue
		}

		if bootstrapErr != nil {
			// NB(r): Last bootstrap failed, since this could be due to transient
			// failure we retry the bootstrap again. This is to avoid operators
			// needing to manually intervene for cases where failures are transient.
			m.log.Warn("retrying bootstrap after backoff",
				zap.Duration("backoff", bootstrapRetryInterval),
				zap.Int("numRetries", i+1))
			m.sleepFn(bootstrapRetryInterval)
			continue
		}

		// No pending bootstraps and last finished successfully.
		break
	}

	// NB(xichen): in order for bootstrapped data to be flushed to disk, a tick
	// needs to happen to drain the in-memory buffers and a consequent flush will
	// flush all the data onto disk. However, this has shown to be too intensive
	// to do immediately after bootstrap due to bootstrapping nodes simultaneously
	// attempting to tick through their series and flushing data, adding significant
	// load to the cluster. It turns out to be better to let ticking happen naturally
	// on its own course so that the load of ticking and flushing is more spread out
	// across the cluster.

	m.lastBootstrapCompletionTime = m.nowFn()
	return result, nil
}

func (m *bootstrapManager) Report() {
	if m.IsBootstrapped() {
		m.status.Update(1)
	} else {
		m.status.Update(0)
	}
}

type bootstrapNamespace struct {
	namespace databaseNamespace
	shards    []databaseShard
}

func (m *bootstrapManager) bootstrap() error {
	// NB(r): construct new instance of the bootstrap process to avoid
	// state being kept around by bootstrappers.
	process, err := m.processProvider.Provide()
	if err != nil {
		return err
	}

	namespaces, err := m.database.GetOwnedNamespaces()
	if err != nil {
		return err
	}

	accmulators := make([]bootstrap.NamespaceDataAccumulator, 0, len(namespaces))
	defer func() {
		// Close all accumulators at bootstrap completion, only error
		// it returns is if already closed, so this is a code bug if ever
		// an error returned.
		for _, accumulator := range accmulators {
			if err := accumulator.Close(); err != nil {
				instrument.EmitAndLogInvariantViolation(m.opts.InstrumentOptions(),
					func(l *zap.Logger) {
						l.Error("could not close bootstrap data accumulator",
							zap.Error(err))
					})
			}
		}
	}()

	start := m.nowFn()
	m.log.Info("bootstrap prepare")

	var (
		bootstrapNamespaces = make([]bootstrapNamespace, len(namespaces))
		prepareWg           sync.WaitGroup
		prepareLock         sync.Mutex
		prepareMultiErr     xerrors.MultiError
	)
	for i, namespace := range namespaces {
		i, namespace := i, namespace
		prepareWg.Add(1)
		go func() {
			shards, err := namespace.PrepareBootstrap()

			prepareLock.Lock()
			defer func() {
				prepareLock.Unlock()
				prepareWg.Done()
			}()

			if err != nil {
				prepareMultiErr = prepareMultiErr.Add(err)
				return
			}

			bootstrapNamespaces[i] = bootstrapNamespace{
				namespace: namespace,
				shards:    shards,
			}
		}()
	}

	prepareWg.Wait()

	if err := prepareMultiErr.FinalError(); err != nil {
		m.log.Error("bootstrap prepare failed", zap.Error(err))
		return err
	}

	var uniqueShards map[uint32]struct{}
	targets := make([]bootstrap.ProcessNamespace, 0, len(namespaces))
	for _, ns := range bootstrapNamespaces {
		bootstrapShards := make([]uint32, 0, len(ns.shards))
		if uniqueShards == nil {
			uniqueShards = make(map[uint32]struct{}, len(ns.shards))
		}

		for _, shard := range ns.shards {
			if shard.IsBootstrapped() {
				continue
			}

			uniqueShards[shard.ID()] = struct{}{}
			bootstrapShards = append(bootstrapShards, shard.ID())
		}

		// Add hooks so that each bootstrapper when it interacts
		// with the namespace and shards during data accumulation
		// gets an up to date view of all the file volumes that
		// actually exist on disk (since bootstrappers can write
		// new blocks to disk).
		hooks := bootstrap.NewNamespaceHooks(bootstrap.NamespaceHooksOptions{
			BootstrapSourceEnd: func() error {
				var wg sync.WaitGroup
				for _, shard := range ns.shards {
					shard := shard
					wg.Add(1)
					go func() {
						shard.UpdateFlushStates()
						wg.Done()
					}()
				}
				wg.Wait()
				return nil
			},
		})

		accumulator := NewDatabaseNamespaceDataAccumulator(ns.namespace)
		accmulators = append(accmulators, accumulator)

		targets = append(targets, bootstrap.ProcessNamespace{
			Metadata:        ns.namespace.Metadata(),
			Shards:          bootstrapShards,
			Hooks:           hooks,
			DataAccumulator: accumulator,
		})
	}

	logFields := []zapcore.Field{
		zap.Int("numShards", len(uniqueShards)),
	}
	m.log.Info("bootstrap started", logFields...)

	// Run the bootstrap.
	bootstrapResult, err := process.Run(start, targets)

	bootstrapDuration := m.nowFn().Sub(start)
	m.bootstrapDuration.Record(bootstrapDuration)
	logFields = append(logFields,
		zap.Duration("bootstrapDuration", bootstrapDuration))

	if err != nil {
		m.log.Error("bootstrap failed",
			append(logFields, zap.Error(err))...)
		return err
	}

	m.log.Info("bootstrap succeeded, marking namespaces complete", logFields...)
	// Use a multi-error here because we want to at least bootstrap
	// as many of the namespaces as possible.
	multiErr := xerrors.NewMultiError()
	for _, namespace := range namespaces {
		id := namespace.ID()
		result, ok := bootstrapResult.Results.Get(id)
		if !ok {
			err := fmt.Errorf("missing namespace from bootstrap result: %v",
				id.String())
			i := m.opts.InstrumentOptions()
			instrument.EmitAndLogInvariantViolation(i, func(l *zap.Logger) {
				l.Error("bootstrap failed",
					append(logFields, zap.Error(err))...)
			})
			return err
		}

		if err := namespace.Bootstrap(result); err != nil {
			m.log.Info("bootstrap error", append(logFields, []zapcore.Field{
				zap.String("namespace", id.String()),
				zap.Error(err),
			}...)...)
			multiErr = multiErr.Add(err)
		}
	}

	if err := multiErr.FinalError(); err != nil {
		m.log.Info("bootstrap namespaces failed",
			append(logFields, zap.Error(err))...)
		return err
	}

	m.log.Info("bootstrap success", logFields...)
	return nil
}
<commit_msg>Add metric for BootstrappedAndDurable (#2210)<commit_after>// Copyright (c) 2016 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package storage

import (
	"errors"
	"fmt"
	"sync"
	"time"

	"github.com/m3db/m3/src/dbnode/clock"
	"github.com/m3db/m3/src/dbnode/storage/bootstrap"
	xerrors "github.com/m3db/m3/src/x/errors"
	"github.com/m3db/m3/src/x/instrument"

	"github.com/uber-go/tally"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
)

var (
	// errNamespaceIsBootstrapping raised when trying to bootstrap a namespace that's being bootstrapped.
	errNamespaceIsBootstrapping = errors.New("namespace is bootstrapping")

	// errNamespaceNotBootstrapped raised when trying to flush/snapshot data for a namespace that's not yet bootstrapped.
	errNamespaceNotBootstrapped = errors.New("namespace is not yet bootstrapped")

	// errShardIsBootstrapping raised when trying to bootstrap a shard that's being bootstrapped.
	errShardIsBootstrapping = errors.New("shard is bootstrapping")

	// errShardNotBootstrappedToFlush raised when trying to flush data for a shard that's not yet bootstrapped.
	errShardNotBootstrappedToFlush = errors.New("shard is not yet bootstrapped to flush")

	// errShardNotBootstrappedToSnapshot raised when trying to snapshot data for a shard that's not yet bootstrapped.
	errShardNotBootstrappedToSnapshot = errors.New("shard is not yet bootstrapped to snapshot")

	// errShardNotBootstrappedToRead raised when trying to read data for a shard that's not yet bootstrapped.
	errShardNotBootstrappedToRead = errors.New("shard is not yet bootstrapped to read")

	// errIndexNotBootstrappedToRead raised when trying to read the index before being bootstrapped.
	errIndexNotBootstrappedToRead = errors.New("index is not yet bootstrapped to read")

	// errBootstrapEnqueued raised when trying to bootstrap and bootstrap becomes enqueued.
	errBootstrapEnqueued = errors.New("database bootstrapping enqueued bootstrap")
)

const (
	bootstrapRetryInterval = 10 * time.Second
)

type bootstrapFn func() error

type bootstrapManager struct {
	sync.RWMutex

	database                    database
	mediator                    databaseMediator
	opts                        Options
	log                         *zap.Logger
	bootstrapFn                 bootstrapFn
	nowFn                       clock.NowFn
	sleepFn                     sleepFn
	processProvider             bootstrap.ProcessProvider
	state                       BootstrapState
	hasPending                  bool
	status                      tally.Gauge
	bootstrapDuration           tally.Timer
	durableStatus               tally.Gauge
	lastBootstrapCompletionTime time.Time
}

func newBootstrapManager(
	database database,
	mediator databaseMediator,
	opts Options,
) databaseBootstrapManager {
	scope := opts.InstrumentOptions().MetricsScope()
	m := &bootstrapManager{
		database:          database,
		mediator:          mediator,
		opts:              opts,
		log:               opts.InstrumentOptions().Logger(),
		nowFn:             opts.ClockOptions().NowFn(),
		sleepFn:           time.Sleep,
		processProvider:   opts.BootstrapProcessProvider(),
		status:            scope.Gauge("bootstrapped"),
		bootstrapDuration: scope.Timer("bootstrap-duration"),
		durableStatus:     scope.Gauge("bootstrapped-durable"),
	}
	m.bootstrapFn = m.bootstrap
	return m
}

func (m *bootstrapManager) IsBootstrapped() bool {
	m.RLock()
	state := m.state
	m.RUnlock()
	return state == Bootstrapped
}

func (m *bootstrapManager) LastBootstrapCompletionTime() (time.Time, bool) {
	return m.lastBootstrapCompletionTime, !m.lastBootstrapCompletionTime.IsZero()
}

func (m *bootstrapManager) Bootstrap() (BootstrapResult, error) {
	m.Lock()
	switch m.state {
	case Bootstrapping:
		// NB(r): Already bootstrapping, now a consequent bootstrap
		// request comes in - we queue this up to bootstrap again
		// once the current bootstrap has completed.
		// This is an edge case that can occur if during either an
		// initial bootstrap or a resharding bootstrap if a new
		// reshard occurs and we need to bootstrap more shards.
		m.hasPending = true
		m.Unlock()
		return BootstrapResult{AlreadyBootstrapping: true}, errBootstrapEnqueued
	default:
		m.state = Bootstrapping
	}
	m.Unlock()

	// NB(xichen): disable filesystem manager before we bootstrap to minimize
	// the impact of file operations on bootstrapping performance
	m.mediator.DisableFileOps()
	defer m.mediator.EnableFileOps()

	// Keep performing bootstraps until none pending and no error returned.
	var result BootstrapResult
	for i := 0; true; i++ {
		// NB(r): Decouple implementation of bootstrap so can override in tests.
		bootstrapErr := m.bootstrapFn()
		if bootstrapErr != nil {
			result.ErrorsBootstrap = append(result.ErrorsBootstrap, bootstrapErr)
		}

		m.Lock()
		currPending := m.hasPending
		if currPending {
			// New bootstrap calls should now enqueue another pending bootstrap
			m.hasPending = false
		} else {
			m.state = Bootstrapped
		}
		m.Unlock()

		if currPending {
			// NB(r): Requires another bootstrap.
			continue
		}

		if bootstrapErr != nil {
			// NB(r): Last bootstrap failed, since this could be due to transient
			// failure we retry the bootstrap again. This is to avoid operators
			// needing to manually intervene for cases where failures are transient.
			m.log.Warn("retrying bootstrap after backoff",
				zap.Duration("backoff", bootstrapRetryInterval),
				zap.Int("numRetries", i+1))
			m.sleepFn(bootstrapRetryInterval)
			continue
		}

		// No pending bootstraps and last finished successfully.
		break
	}

	// NB(xichen): in order for bootstrapped data to be flushed to disk, a tick
	// needs to happen to drain the in-memory buffers and a consequent flush will
	// flush all the data onto disk. However, this has shown to be too intensive
	// to do immediately after bootstrap due to bootstrapping nodes simultaneously
	// attempting to tick through their series and flushing data, adding significant
	// load to the cluster. It turns out to be better to let ticking happen naturally
	// on its own course so that the load of ticking and flushing is more spread out
	// across the cluster.

	m.lastBootstrapCompletionTime = m.nowFn()
	return result, nil
}

func (m *bootstrapManager) Report() {
	if m.IsBootstrapped() {
		m.status.Update(1)
	} else {
		m.status.Update(0)
	}

	if m.database.IsBootstrappedAndDurable() {
		m.durableStatus.Update(1)
	} else {
		m.durableStatus.Update(0)
	}
}

type bootstrapNamespace struct {
	namespace databaseNamespace
	shards    []databaseShard
}

func (m *bootstrapManager) bootstrap() error {
	// NB(r): construct new instance of the bootstrap process to avoid
	// state being kept around by bootstrappers.
	process, err := m.processProvider.Provide()
	if err != nil {
		return err
	}

	namespaces, err := m.database.GetOwnedNamespaces()
	if err != nil {
		return err
	}

	accmulators := make([]bootstrap.NamespaceDataAccumulator, 0, len(namespaces))
	defer func() {
		// Close all accumulators at bootstrap completion, only error
		// it returns is if already closed, so this is a code bug if ever
		// an error returned.
		for _, accumulator := range accmulators {
			if err := accumulator.Close(); err != nil {
				instrument.EmitAndLogInvariantViolation(m.opts.InstrumentOptions(),
					func(l *zap.Logger) {
						l.Error("could not close bootstrap data accumulator",
							zap.Error(err))
					})
			}
		}
	}()

	start := m.nowFn()
	m.log.Info("bootstrap prepare")

	var (
		bootstrapNamespaces = make([]bootstrapNamespace, len(namespaces))
		prepareWg           sync.WaitGroup
		prepareLock         sync.Mutex
		prepareMultiErr     xerrors.MultiError
	)
	for i, namespace := range namespaces {
		i, namespace := i, namespace
		prepareWg.Add(1)
		go func() {
			shards, err := namespace.PrepareBootstrap()

			prepareLock.Lock()
			defer func() {
				prepareLock.Unlock()
				prepareWg.Done()
			}()

			if err != nil {
				prepareMultiErr = prepareMultiErr.Add(err)
				return
			}

			bootstrapNamespaces[i] = bootstrapNamespace{
				namespace: namespace,
				shards:    shards,
			}
		}()
	}

	prepareWg.Wait()

	if err := prepareMultiErr.FinalError(); err != nil {
		m.log.Error("bootstrap prepare failed", zap.Error(err))
		return err
	}

	var uniqueShards map[uint32]struct{}
	targets := make([]bootstrap.ProcessNamespace, 0, len(namespaces))
	for _, ns := range bootstrapNamespaces {
		bootstrapShards := make([]uint32, 0, len(ns.shards))
		if uniqueShards == nil {
			uniqueShards = make(map[uint32]struct{}, len(ns.shards))
		}

		for _, shard := range ns.shards {
			if shard.IsBootstrapped() {
				continue
			}

			uniqueShards[shard.ID()] = struct{}{}
			bootstrapShards = append(bootstrapShards, shard.ID())
		}

		// Add hooks so that each bootstrapper when it interacts
		// with the namespace and shards during data accumulation
		// gets an up to date view of all the file volumes that
		// actually exist on disk (since bootstrappers can write
		// new blocks to disk).
		hooks := bootstrap.NewNamespaceHooks(bootstrap.NamespaceHooksOptions{
			BootstrapSourceEnd: func() error {
				var wg sync.WaitGroup
				for _, shard := range ns.shards {
					shard := shard
					wg.Add(1)
					go func() {
						shard.UpdateFlushStates()
						wg.Done()
					}()
				}
				wg.Wait()
				return nil
			},
		})

		accumulator := NewDatabaseNamespaceDataAccumulator(ns.namespace)
		accmulators = append(accmulators, accumulator)

		targets = append(targets, bootstrap.ProcessNamespace{
			Metadata:        ns.namespace.Metadata(),
			Shards:          bootstrapShards,
			Hooks:           hooks,
			DataAccumulator: accumulator,
		})
	}

	logFields := []zapcore.Field{
		zap.Int("numShards", len(uniqueShards)),
	}
	m.log.Info("bootstrap started", logFields...)

	// Run the bootstrap.
	bootstrapResult, err := process.Run(start, targets)

	bootstrapDuration := m.nowFn().Sub(start)
	m.bootstrapDuration.Record(bootstrapDuration)
	logFields = append(logFields,
		zap.Duration("bootstrapDuration", bootstrapDuration))

	if err != nil {
		m.log.Error("bootstrap failed",
			append(logFields, zap.Error(err))...)
		return err
	}

	m.log.Info("bootstrap succeeded, marking namespaces complete", logFields...)
	// Use a multi-error here because we want to at least bootstrap
	// as many of the namespaces as possible.
	multiErr := xerrors.NewMultiError()
	for _, namespace := range namespaces {
		id := namespace.ID()
		result, ok := bootstrapResult.Results.Get(id)
		if !ok {
			err := fmt.Errorf("missing namespace from bootstrap result: %v",
				id.String())
			i := m.opts.InstrumentOptions()
			instrument.EmitAndLogInvariantViolation(i, func(l *zap.Logger) {
				l.Error("bootstrap failed",
					append(logFields, zap.Error(err))...)
			})
			return err
		}

		if err := namespace.Bootstrap(result); err != nil {
			m.log.Info("bootstrap error", append(logFields, []zapcore.Field{
				zap.String("namespace", id.String()),
				zap.Error(err),
			}...)...)
			multiErr = multiErr.Add(err)
		}
	}

	if err := multiErr.FinalError(); err != nil {
		m.log.Info("bootstrap namespaces failed",
			append(logFields, zap.Error(err))...)
		return err
	}

	m.log.Info("bootstrap success", logFields...)
	return nil
}
<|endoftext|>


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

package gosym

import (
	"debug/elf"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
	"testing"
)

var (
	pclineTempDir    string
	pclinetestBinary string
)

func dotest(self bool) bool {
	// For now, only works on amd64 platforms.
	if runtime.GOARCH != "amd64" {
		return false
	}
	// Self test reads test binary; only works on Linux.
	if self && runtime.GOOS != "linux" {
		return false
	}
	// Command below expects "sh", so Unix.
	if runtime.GOOS == "windows" || runtime.GOOS == "plan9" {
		return false
	}
	if pclinetestBinary != "" {
		return true
	}
	var err error
	pclineTempDir, err = ioutil.TempDir("", "pclinetest")
	if err != nil {
		panic(err)
	}
	if strings.Contains(pclineTempDir, " ") {
		panic("unexpected space in tempdir")
	}
	// This command builds pclinetest from pclinetest.asm;
	// the resulting binary looks like it was built from pclinetest.s,
	// but we have renamed it to keep it away from the go tool.
	pclinetestBinary = filepath.Join(pclineTempDir, "pclinetest")
	command := fmt.Sprintf("go tool asm -o %s.6 pclinetest.asm && go tool 6l -H linux -E main -o %s %s.6",
		pclinetestBinary, pclinetestBinary, pclinetestBinary)
	cmd := exec.Command("sh", "-c", command)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if err := cmd.Run(); err != nil {
		panic(err)
	}
	return true
}

func endtest() {
	if pclineTempDir != "" {
		os.RemoveAll(pclineTempDir)
		pclineTempDir = ""
		pclinetestBinary = ""
	}
}

func getTable(t *testing.T) *Table {
	f, tab := crack(os.Args[0], t)
	f.Close()
	return tab
}

func crack(file string, t *testing.T) (*elf.File, *Table) {
	// Open self
	f, err := elf.Open(file)
	if err != nil {
		t.Fatal(err)
	}
	return parse(file, f, t)
}

func parse(file string, f *elf.File, t *testing.T) (*elf.File, *Table) {
	symdat, err := f.Section(".gosymtab").Data()
	if err != nil {
		f.Close()
		t.Fatalf("reading %s gosymtab: %v", file, err)
	}
	pclndat, err := f.Section(".gopclntab").Data()
	if err != nil {
		f.Close()
		t.Fatalf("reading %s gopclntab: %v", file, err)
	}

	pcln := NewLineTable(pclndat, f.Section(".text").Addr)
	tab, err := NewTable(symdat, pcln)
	if err != nil {
		f.Close()
		t.Fatalf("parsing %s gosymtab: %v", file, err)
	}

	return f, tab
}

var goarch = os.Getenv("O")

func TestLineFromAline(t *testing.T) {
	if !dotest(true) {
		return
	}
	defer endtest()

	tab := getTable(t)
	if tab.go12line != nil {
		// aline's don't exist in the Go 1.2 table.
		t.Skip("not relevant to Go 1.2 symbol table")
	}

	// Find the sym package
	pkg := tab.LookupFunc("debug/gosym.TestLineFromAline").Obj
	if pkg == nil {
		t.Fatalf("nil pkg")
	}

	// Walk every absolute line and ensure that we hit every
	// source line monotonically
	lastline := make(map[string]int)
	final := -1
	for i := 0; i < 10000; i++ {
		path, line := pkg.lineFromAline(i)
		// Check for end of object
		if path == "" {
			if final == -1 {
				final = i - 1
			}
			continue
		} else if final != -1 {
			t.Fatalf("reached end of package at absolute line %d, but absolute line %d mapped to %s:%d", final, i, path, line)
		}
		// It's okay to see files multiple times (e.g., sys.a)
		if line == 1 {
			lastline[path] = 1
			continue
		}
		// Check that the is the next line in path
		ll, ok := lastline[path]
		if !ok {
			t.Errorf("file %s starts on line %d", path, line)
		} else if line != ll+1 {
			t.Fatalf("expected next line of file %s to be %d, got %d", path, ll+1, line)
		}
		lastline[path] = line
	}
	if final == -1 {
		t.Errorf("never reached end of object")
	}
}

func TestLineAline(t *testing.T) {
	if !dotest(true) {
		return
	}
	defer endtest()

	tab := getTable(t)
	if tab.go12line != nil {
		// aline's don't exist in the Go 1.2 table.
		t.Skip("not relevant to Go 1.2 symbol table")
	}

	for _, o := range tab.Files {
		// A source file can appear multiple times in a
		// object.  alineFromLine will always return alines in
		// the first file, so track which lines we've seen.
		found := make(map[string]int)
		for i := 0; i < 1000; i++ {
			path, line := o.lineFromAline(i)
			if path == "" {
				break
			}

			// cgo files are full of 'Z' symbols, which we don't handle
			if len(path) > 4 && path[len(path)-4:] == ".cgo" {
				continue
			}

			if minline, ok := found[path]; path != "" && ok {
				if minline >= line {
					// We've already covered this file
					continue
				}
			}
			found[path] = line

			a, err := o.alineFromLine(path, line)
			if err != nil {
				t.Errorf("absolute line %d in object %s maps to %s:%d, but mapping that back gives error %s", i, o.Paths[0].Name, path, line, err)
			} else if a != i {
				t.Errorf("absolute line %d in object %s maps to %s:%d, which maps back to absolute line %d\n", i, o.Paths[0].Name, path, line, a)
			}
		}
	}
}

func TestPCLine(t *testing.T) {
	if !dotest(false) {
		return
	}
	defer endtest()

	f, tab := crack(pclinetestBinary, t)
	text := f.Section(".text")
	textdat, err := text.Data()
	if err != nil {
		t.Fatalf("reading .text: %v", err)
	}

	// Test PCToLine
	sym := tab.LookupFunc("linefrompc")
	wantLine := 0
	for pc := sym.Entry; pc < sym.End; pc++ {
		off := pc - text.Addr // TODO(rsc): should not need off; bug in 8g
		if textdat[off] == 255 {
			break
		}
		wantLine += int(textdat[off])
		t.Logf("off is %d %#x (max %d)", off, textdat[off], sym.End-pc)
		file, line, fn := tab.PCToLine(pc)
		if fn == nil {
			t.Errorf("failed to get line of PC %#x", pc)
		} else if !strings.HasSuffix(file, "pclinetest.asm") || line != wantLine || fn != sym {
			t.Errorf("PCToLine(%#x) = %s:%d (%s), want %s:%d (%s)", pc, file, line, fn.Name, "pclinetest.asm", wantLine, sym.Name)
		}
	}

	// Test LineToPC
	sym = tab.LookupFunc("pcfromline")
	lookupline := -1
	wantLine = 0
	off := uint64(0) // TODO(rsc): should not need off; bug in 8g
	for pc := sym.Value; pc < sym.End; pc += 2 + uint64(textdat[off]) {
		file, line, fn := tab.PCToLine(pc)
		off = pc - text.Addr
		if textdat[off] == 255 {
			break
		}
		wantLine += int(textdat[off])
		if line != wantLine {
			t.Errorf("expected line %d at PC %#x in pcfromline, got %d", wantLine, pc, line)
			off = pc + 1 - text.Addr
			continue
		}
		if lookupline == -1 {
			lookupline = line
		}
		for ; lookupline <= line; lookupline++ {
			pc2, fn2, err := tab.LineToPC(file, lookupline)
			if lookupline != line {
				// Should be nothing on this line
				if err == nil {
					t.Errorf("expected no PC at line %d, got %#x (%s)", lookupline, pc2, fn2.Name)
				}
			} else if err != nil {
				t.Errorf("failed to get PC of line %d: %s", lookupline, err)
			} else if pc != pc2 {
				t.Errorf("expected PC %#x (%s) at line %d, got PC %#x (%s)", pc, fn.Name, line, pc2, fn2.Name)
			}
		}
		off = pc + 1 - text.Addr
	}
}
<commit_msg>debug/gosym: skip tests when .gosymtab section not found<commit_after>// Copyright 2009 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package gosym

import (
	"debug/elf"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
	"testing"
)

var (
	pclineTempDir    string
	pclinetestBinary string
)

func dotest(self bool) bool {
	// For now, only works on amd64 platforms.
	if runtime.GOARCH != "amd64" {
		return false
	}
	// Self test reads test binary; only works on Linux.
	if self && runtime.GOOS != "linux" {
		return false
	}
	// Command below expects "sh", so Unix.
	if runtime.GOOS == "windows" || runtime.GOOS == "plan9" {
		return false
	}
	if pclinetestBinary != "" {
		return true
	}
	var err error
	pclineTempDir, err = ioutil.TempDir("", "pclinetest")
	if err != nil {
		panic(err)
	}
	if strings.Contains(pclineTempDir, " ") {
		panic("unexpected space in tempdir")
	}
	// This command builds pclinetest from pclinetest.asm;
	// the resulting binary looks like it was built from pclinetest.s,
	// but we have renamed it to keep it away from the go tool.
	pclinetestBinary = filepath.Join(pclineTempDir, "pclinetest")
	command := fmt.Sprintf("go tool asm -o %s.6 pclinetest.asm && go tool 6l -H linux -E main -o %s %s.6",
		pclinetestBinary, pclinetestBinary, pclinetestBinary)
	cmd := exec.Command("sh", "-c", command)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if err := cmd.Run(); err != nil {
		panic(err)
	}
	return true
}

func endtest() {
	if pclineTempDir != "" {
		os.RemoveAll(pclineTempDir)
		pclineTempDir = ""
		pclinetestBinary = ""
	}
}

func getTable(t *testing.T) *Table {
	f, tab := crack(os.Args[0], t)
	f.Close()
	return tab
}

func crack(file string, t *testing.T) (*elf.File, *Table) {
	// Open self
	f, err := elf.Open(file)
	if err != nil {
		t.Fatal(err)
	}
	return parse(file, f, t)
}

func parse(file string, f *elf.File, t *testing.T) (*elf.File, *Table) {
	s := f.Section(".gosymtab")
	if s == nil {
		t.Skip("no .gosymtab section")
	}
	symdat, err := s.Data()
	if err != nil {
		f.Close()
		t.Fatalf("reading %s gosymtab: %v", file, err)
	}
	pclndat, err := f.Section(".gopclntab").Data()
	if err != nil {
		f.Close()
		t.Fatalf("reading %s gopclntab: %v", file, err)
	}

	pcln := NewLineTable(pclndat, f.Section(".text").Addr)
	tab, err := NewTable(symdat, pcln)
	if err != nil {
		f.Close()
		t.Fatalf("parsing %s gosymtab: %v", file, err)
	}

	return f, tab
}

var goarch = os.Getenv("O")

func TestLineFromAline(t *testing.T) {
	if !dotest(true) {
		return
	}
	defer endtest()

	tab := getTable(t)
	if tab.go12line != nil {
		// aline's don't exist in the Go 1.2 table.
		t.Skip("not relevant to Go 1.2 symbol table")
	}

	// Find the sym package
	pkg := tab.LookupFunc("debug/gosym.TestLineFromAline").Obj
	if pkg == nil {
		t.Fatalf("nil pkg")
	}

	// Walk every absolute line and ensure that we hit every
	// source line monotonically
	lastline := make(map[string]int)
	final := -1
	for i := 0; i < 10000; i++ {
		path, line := pkg.lineFromAline(i)
		// Check for end of object
		if path == "" {
			if final == -1 {
				final = i - 1
			}
			continue
		} else if final != -1 {
			t.Fatalf("reached end of package at absolute line %d, but absolute line %d mapped to %s:%d", final, i, path, line)
		}
		// It's okay to see files multiple times (e.g., sys.a)
		if line == 1 {
			lastline[path] = 1
			continue
		}
		// Check that the is the next line in path
		ll, ok := lastline[path]
		if !ok {
			t.Errorf("file %s starts on line %d", path, line)
		} else if line != ll+1 {
			t.Fatalf("expected next line of file %s to be %d, got %d", path, ll+1, line)
		}
		lastline[path] = line
	}
	if final == -1 {
		t.Errorf("never reached end of object")
	}
}

func TestLineAline(t *testing.T) {
	if !dotest(true) {
		return
	}
	defer endtest()

	tab := getTable(t)
	if tab.go12line != nil {
		// aline's don't exist in the Go 1.2 table.
		t.Skip("not relevant to Go 1.2 symbol table")
	}

	for _, o := range tab.Files {
		// A source file can appear multiple times in a
		// object.  alineFromLine will always return alines in
		// the first file, so track which lines we've seen.
		found := make(map[string]int)
		for i := 0; i < 1000; i++ {
			path, line := o.lineFromAline(i)
			if path == "" {
				break
			}

			// cgo files are full of 'Z' symbols, which we don't handle
			if len(path) > 4 && path[len(path)-4:] == ".cgo" {
				continue
			}

			if minline, ok := found[path]; path != "" && ok {
				if minline >= line {
					// We've already covered this file
					continue
				}
			}
			found[path] = line

			a, err := o.alineFromLine(path, line)
			if err != nil {
				t.Errorf("absolute line %d in object %s maps to %s:%d, but mapping that back gives error %s", i, o.Paths[0].Name, path, line, err)
			} else if a != i {
				t.Errorf("absolute line %d in object %s maps to %s:%d, which maps back to absolute line %d\n", i, o.Paths[0].Name, path, line, a)
			}
		}
	}
}

func TestPCLine(t *testing.T) {
	if !dotest(false) {
		return
	}
	defer endtest()

	f, tab := crack(pclinetestBinary, t)
	text := f.Section(".text")
	textdat, err := text.Data()
	if err != nil {
		t.Fatalf("reading .text: %v", err)
	}

	// Test PCToLine
	sym := tab.LookupFunc("linefrompc")
	wantLine := 0
	for pc := sym.Entry; pc < sym.End; pc++ {
		off := pc - text.Addr // TODO(rsc): should not need off; bug in 8g
		if textdat[off] == 255 {
			break
		}
		wantLine += int(textdat[off])
		t.Logf("off is %d %#x (max %d)", off, textdat[off], sym.End-pc)
		file, line, fn := tab.PCToLine(pc)
		if fn == nil {
			t.Errorf("failed to get line of PC %#x", pc)
		} else if !strings.HasSuffix(file, "pclinetest.asm") || line != wantLine || fn != sym {
			t.Errorf("PCToLine(%#x) = %s:%d (%s), want %s:%d (%s)", pc, file, line, fn.Name, "pclinetest.asm", wantLine, sym.Name)
		}
	}

	// Test LineToPC
	sym = tab.LookupFunc("pcfromline")
	lookupline := -1
	wantLine = 0
	off := uint64(0) // TODO(rsc): should not need off; bug in 8g
	for pc := sym.Value; pc < sym.End; pc += 2 + uint64(textdat[off]) {
		file, line, fn := tab.PCToLine(pc)
		off = pc - text.Addr
		if textdat[off] == 255 {
			break
		}
		wantLine += int(textdat[off])
		if line != wantLine {
			t.Errorf("expected line %d at PC %#x in pcfromline, got %d", wantLine, pc, line)
			off = pc + 1 - text.Addr
			continue
		}
		if lookupline == -1 {
			lookupline = line
		}
		for ; lookupline <= line; lookupline++ {
			pc2, fn2, err := tab.LineToPC(file, lookupline)
			if lookupline != line {
				// Should be nothing on this line
				if err == nil {
					t.Errorf("expected no PC at line %d, got %#x (%s)", lookupline, pc2, fn2.Name)
				}
			} else if err != nil {
				t.Errorf("failed to get PC of line %d: %s", lookupline, err)
			} else if pc != pc2 {
				t.Errorf("expected PC %#x (%s) at line %d, got PC %#x (%s)", pc, fn.Name, line, pc2, fn2.Name)
			}
		}
		off = pc + 1 - text.Addr
	}
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"strings"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/ec2metadata"
	"github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds"

	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/aws/aws-sdk-go/service/s3"
)

type awsHostContext struct {
	Creds   *credentials.Credentials
	Id      string
	Region  string
}

var DEBUG bool
func debug(m ...interface{}) {
	if(DEBUG) {
		fmt.Println(m...)
	}
}

func main() {
	// currently not using any of the client flags
	_ = flag.String("u", "", "The user account")
	_ = flag.String("k", "", "The public key proffered by the client")
	_ = flag.String("t", "", "The key encryption type")
	_ = flag.String("f", "", "The fingerprint of the public key")
	groupTag := flag.String("group_tag", "access-groups", "The instance tag with csv ssh sccess groups in it")
	s3Bucket := flag.String("s3_bucket", "keys", "The bucket where the access group public keys are stored")
	s3Region := flag.String("s3_region", "eu-west-1", "The region in which the bucket is located")
	debugOutput := flag.Bool("debug", false, "Enable debug output")
	flag.Parse()

	if(*debugOutput) {
		DEBUG = true
		debug("debug output enabled")
		debug("grouptag: ", *groupTag)
		debug("bucket:   ", *s3Bucket)
		debug("region:   ", *s3Region)
		debug("")
	}

	// get aws host context
	hctx, err := getAwsHostContext()
	if err != nil {
		debug(err.Error())
		os.Exit(1)
	}

	// get the list of access groups the intance belongs to
	accessGroups, err := getInstanceAccessGroups(hctx, *groupTag)
	if err != nil {
		debug(err.Error())
		os.Exit(1)
	}

	// get the authorized_keys files for those instances
	authorizedKeys, err := getAccessKeys(hctx, *s3Bucket, *s3Region, accessGroups)
	if err != nil {
		debug(err.Error())
		os.Exit(1)
	}

	// print all authorized_keys
	debug("print all group keys:")
	for _, k := range authorizedKeys {
		fmt.Print(k)
	}
}

func getAwsHostContext() (*awsHostContext, error) {
	//setup
	hctx := new(awsHostContext)
	cfg := aws.NewConfig()
	svc := ec2metadata.New(session.New(), cfg)
	p := &ec2rolecreds.EC2RoleProvider{
		Client: svc,
	}
	//get creds
	hctx.Creds = credentials.NewCredentials(p)
	//get instance id
	id, err := svc.GetMetadata("instance-id")
	if err != nil {
		return nil, err
	}
	hctx.Id = id
	debug("instance-id:", id)
	//get region
	region, err := svc.Region()
	if err != nil {
		return nil, err
	}
	hctx.Region = region
	debug("region:     ", region)
	return hctx, nil
}

func getInstanceAccessGroups(hctx *awsHostContext, tag string) ([]string, error) {
	// setup
	svc := ec2.New(session.New(aws.NewConfig().WithCredentials(hctx.Creds).WithRegion(hctx.Region)))
	params := &ec2.DescribeTagsInput{
		Filters: []*ec2.Filter{
			{
				Name: aws.String("resource-id"),
				Values: []*string{ aws.String(hctx.Id) },
			},
			{
				Name: aws.String("key"),
				Values: []*string{ aws.String(tag) },
			},
		},
	}
	// fetch tags
	debug("requesting tag: " + tag + ", from resource: " + hctx.Id)
	resp, err := svc.DescribeTags(params)
	if err != nil {
		return []string{}, err
	}
	debug("recieved:", resp.Tags)

	// valiade response
	if len(resp.Tags) == 0 {
		return []string{}, errors.New("tag " + tag + " does not exist!")
	}
	// parse and return response
	var out []string
	for _, s := range strings.Split(*resp.Tags[0].Value, ",") {
		out = append(out, strings.TrimSpace(s))
	}
	debug("access groups:", out)
	return out, nil
}

func getAccessKeys(hctx *awsHostContext, s3Bucket, s3Region string, accessGroups []string) ([]string, error) {
	// setup
	svc := s3.New(session.New(aws.NewConfig().WithCredentials(hctx.Creds).WithRegion(s3Region)))

	var out []string
	// fetch authorized_keys files
	for _, group := range accessGroups {
		params := &s3.GetObjectInput{
			Bucket:  aws.String(s3Bucket),
			Key:     aws.String(group + "/authorized_keys"),  // Required
		}
		debug("requesting authorized_keys file: s3://" + s3Bucket + "/" + group + "/authorized_keys")
		resp, err := svc.GetObject(params)
		if err != nil {
			debug(err.Error())
			continue
		}
		debug("retrieved, appending to output")
		if b, err := ioutil.ReadAll(resp.Body); err == nil {
		out = append(out, string(b))
		}
	}

	return out, nil
}
<commit_msg>Fix spelling of group_tag description<commit_after>package main

import (
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"strings"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/ec2metadata"
	"github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds"

	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/aws/aws-sdk-go/service/s3"
)

type awsHostContext struct {
	Creds   *credentials.Credentials
	Id      string
	Region  string
}

var DEBUG bool
func debug(m ...interface{}) {
	if(DEBUG) {
		fmt.Println(m...)
	}
}

func main() {
	// currently not using any of the client flags
	_ = flag.String("u", "", "The user account")
	_ = flag.String("k", "", "The public key proffered by the client")
	_ = flag.String("t", "", "The key encryption type")
	_ = flag.String("f", "", "The fingerprint of the public key")
	groupTag := flag.String("group_tag", "access-groups", "The instance tag with csv ssh access groups in it")
	s3Bucket := flag.String("s3_bucket", "keys", "The bucket where the access group public keys are stored")
	s3Region := flag.String("s3_region", "eu-west-1", "The region in which the bucket is located")
	debugOutput := flag.Bool("debug", false, "Enable debug output")
	flag.Parse()

	if(*debugOutput) {
		DEBUG = true
		debug("debug output enabled")
		debug("grouptag: ", *groupTag)
		debug("bucket:   ", *s3Bucket)
		debug("region:   ", *s3Region)
		debug("")
	}

	// get aws host context
	hctx, err := getAwsHostContext()
	if err != nil {
		debug(err.Error())
		os.Exit(1)
	}

	// get the list of access groups the intance belongs to
	accessGroups, err := getInstanceAccessGroups(hctx, *groupTag)
	if err != nil {
		debug(err.Error())
		os.Exit(1)
	}

	// get the authorized_keys files for those instances
	authorizedKeys, err := getAccessKeys(hctx, *s3Bucket, *s3Region, accessGroups)
	if err != nil {
		debug(err.Error())
		os.Exit(1)
	}

	// print all authorized_keys
	debug("print all group keys:")
	for _, k := range authorizedKeys {
		fmt.Print(k)
	}
}

func getAwsHostContext() (*awsHostContext, error) {
	//setup
	hctx := new(awsHostContext)
	cfg := aws.NewConfig()
	svc := ec2metadata.New(session.New(), cfg)
	p := &ec2rolecreds.EC2RoleProvider{
		Client: svc,
	}
	//get creds
	hctx.Creds = credentials.NewCredentials(p)
	//get instance id
	id, err := svc.GetMetadata("instance-id")
	if err != nil {
		return nil, err
	}
	hctx.Id = id
	debug("instance-id:", id)
	//get region
	region, err := svc.Region()
	if err != nil {
		return nil, err
	}
	hctx.Region = region
	debug("region:     ", region)
	return hctx, nil
}

func getInstanceAccessGroups(hctx *awsHostContext, tag string) ([]string, error) {
	// setup
	svc := ec2.New(session.New(aws.NewConfig().WithCredentials(hctx.Creds).WithRegion(hctx.Region)))
	params := &ec2.DescribeTagsInput{
		Filters: []*ec2.Filter{
			{
				Name: aws.String("resource-id"),
				Values: []*string{ aws.String(hctx.Id) },
			},
			{
				Name: aws.String("key"),
				Values: []*string{ aws.String(tag) },
			},
		},
	}
	// fetch tags
	debug("requesting tag: " + tag + ", from resource: " + hctx.Id)
	resp, err := svc.DescribeTags(params)
	if err != nil {
		return []string{}, err
	}
	debug("received:", resp.Tags)

	// valiade response
	if len(resp.Tags) == 0 {
		return []string{}, errors.New("tag " + tag + " does not exist!")
	}
	// parse and return response
	var out []string
	for _, s := range strings.Split(*resp.Tags[0].Value, ",") {
		out = append(out, strings.TrimSpace(s))
	}
	debug("access groups:", out)
	return out, nil
}

func getAccessKeys(hctx *awsHostContext, s3Bucket, s3Region string, accessGroups []string) ([]string, error) {
	// setup
	svc := s3.New(session.New(aws.NewConfig().WithCredentials(hctx.Creds).WithRegion(s3Region)))

	var out []string
	// fetch authorized_keys files
	for _, group := range accessGroups {
		params := &s3.GetObjectInput{
			Bucket:  aws.String(s3Bucket),
			Key:     aws.String(group + "/authorized_keys"),  // Required
		}
		debug("requesting authorized_keys file: s3://" + s3Bucket + "/" + group + "/authorized_keys")
		resp, err := svc.GetObject(params)
		if err != nil {
			debug(err.Error())
			continue
		}
		debug("retrieved, appending to output")
		if b, err := ioutil.ReadAll(resp.Body); err == nil {
		out = append(out, string(b))
		}
	}

	return out, nil
}
<|endoftext|>


<commit_before>// Copyright 2015 The Pivo Authors. All rights reserved.
// Use of this source code is governed by a Simplified BSD
// license that can be found in the LICENSE file.

package pivo

import (
	"errors"
	"net"
	"time"
)

// The Pivo package version numbers
const Version = "2.0.0"

// Default value for the join rate limit.
// Currently defined as a maximum of 64 per seconds.
const DefaultJoinLimitRateInterval = time.Second / 64

// Default value for the allowed burst over the join limit.
// Currently defined as a maximum of 32 more connections.
const DefaultJoinLimitRateBurst = 32

// Default value for the size of the join queue.
// Currently defined as 256 pending connections before new
// ones are discarded.
const DefaultJoinMaxQueueSize = 256

// Marker for binary message type
const IsBinaryMessage = 1

// Marker for text message type
const IsTextMessage = 0

// Error is thrown when the join queue is full.
var ErrJoinQueueIsFull = errors.New("join queue is full")

// Error is thrown when a connector has its port buffer full.
var ErrPortBufferIsFull = errors.New("port buffer is full")

// Error is thrown when unexpected code path is reached.
var ErrShouldNotReachThis = errors.New("should not reach this")

// Connector is the interface that wraps the basic methods needed
// to use a connector with the hub.
type Connector interface {
	Close(error) error
	Protocol() string
	RemoteAddr() net.Addr
	Send(*Message) error
}

// OnBinaryReader is called when binary data is read from
// a connector.
type OnBinaryReader interface {
	OnBinaryRead([]byte) error
}

// OnBinaryReadCloser is the interface that wraps basic methods
// both for reading binary data and getting notified about a
// closed connection.
type OnBinaryReadCloser interface {
	OnBinaryReader
	OnCloser
}

// OnCloser is the interface that requires a method to call upon
// disconnection of a connector.
type OnCloser interface {
	OnClose(error) error
}

// OnReader is the interface that wraps the methods called when
// data is read from a connector.
type OnReader interface {
	OnBinaryReader
	OnTextReader
}

// OnReadCloser wraps the basic methods needed to be notified
// when data has been read or the connector has closed.
type OnReadCloser interface {
	OnCloser
	OnReader
}

// OnTextReader is called when text data is read from
// a connector.
type OnTextReader interface {
	OnTextRead(string) error
}

// OnTextReadCloser is the interface that wraps basic methods
// both for reading text data and getting notified about a
// closed connection.
type OnTextReadCloser interface {
	OnTextReader
	OnCloser
}

// A Hub is a collection of connectors with some specified settings.
type Hub struct {
	// The interval at which new connections are processed.
	// A Duration of time.Second / 100 would mean hundred
	// of new connections are processed in a second.
	JoinLimitRateInterval time.Duration

	// The size of the burst allowed when the join rate limit
	// is reached. A value of 10 would mean that 10 extra
	// connections may wait to join the hub.
	JoinLimitRateBurst uint

	// The maximum number of new connections waiting to
	// join the hub before ErrJoinQueueIsFull is being
	// thrown.
	JoinMaxQueueSize uint

	broadcast Port           // Broadcast channel
	join      chan Connector // Join the connector
	leave     chan Connector // Disjoin the connector
	ports     Ports          // Map of connected connectors
	stop      chan bool      // Stop the hub
	timeslots chan bool      // Join burst timeslots
}

// OnHubBroadcaster is triggered before the message is delivered.
// Callee may alter the message in any way as well as
// provide a different list of recipients ports.
// If error is thrown, broadcast will not occur at all.
type OnHubBroadcaster interface {
	OnHubBroadcast(*Message) (Ports, error)
}

// OnHubJoiner is triggered before a connector may join the hub.
// If error is thrown, join will not occur at all.
type OnHubJoiner interface {
	OnHubJoin(Connector) error
}

// OnHubLeaver is triggered before a connector disjoin the hub.
// Nothing may prevent this from happening at this stage.
type OnHubLeaver interface {
	OnHubLeave(Connector)
}

// OnHubCloser is triggered right before the hub stops.
// The map of connected connectors is provided and is
// guaranteed to be accurate at the time the callee has it.
type OnHubCloser interface {
	OnHubClose(Ports)
}

// Message is a data structure with bytes of data, type of data,
// and its original connector.
type Message struct {
	Data []byte    // Bytes of data
	From Connector // Original connector
	Type int       // Type of data
}

// Port is a channel transporting a pointer to a Message.
type Port chan *Message

// Ports is a map of Connectors that have joined the hub.
type Ports map[Connector]bool

// throttler is the timeslots ticker.
type throttler struct {
	stop chan bool // Stop the throttler
}

// BinaryMessage formats a binary message and returns
// a pointer to it.
func BinaryMessage(from Connector, bin []byte) *Message {
	return &Message{bin, from, IsBinaryMessage}
}

// DefaultHub instantiate a new hub with default settings.
func DefaultHub() *Hub {
	return &Hub{
		JoinLimitRateBurst:    DefaultJoinLimitRateBurst,
		JoinLimitRateInterval: DefaultJoinLimitRateInterval,
		JoinMaxQueueSize:      DefaultJoinMaxQueueSize,
	}
}

// TextMessage formats a text message and returns
// a pointer to it.
func TextMessage(from Connector, text string) *Message {
	return &Message{[]byte(text), from, IsTextMessage}
}

// throttler fills the timeslots channel at specified interval.
func (h *Hub) throttler(rate time.Duration) *throttler {
	throttler := &throttler{stop: make(chan bool)}
	go func() {
		h.timeslots = make(chan bool, h.JoinLimitRateBurst+1)
		ticker := time.NewTicker(rate)
		defer func() { ticker.Stop(); close(h.timeslots) }()
		for {
			select {
			case <-throttler.stop:
				return
			case <-ticker.C:
				select {
				case h.timeslots <- true:
				default:
				}
			}
		}
	}()
	return throttler
}

// Broadcast sends a message to all connectors on the hub.
func (h *Hub) Broadcast(m *Message) { h.broadcast <- m }

// Leave disjoins the given connector from the hub.
func (h *Hub) Leave(c Connector) { h.leave <- c }

// Stop brings the hub down.
func (h *Hub) Stop() { h.stop <- true }

// Join adds the given connector to the hub.
func (h *Hub) Join(c Connector) error {
	select {
	case h.join <- c:
	default:
		return ErrJoinQueueIsFull
	}
	return nil
}

// StartAndServe serves the hub until Stop() is requested.
func (h *Hub) StartAndServe(trigger interface{}) error {
	var throttler *throttler

	// Start a throttler if necessary
	if h.JoinLimitRateInterval > 0 {
		throttler = h.throttler(h.JoinLimitRateInterval)
		defer func() { throttler.stop <- true }()
	}

	// Initialize control channels
	h.broadcast = make(Port)
	h.join = make(chan Connector, h.JoinMaxQueueSize)
	h.leave = make(chan Connector)
	h.stop = make(chan bool)

	// Initialize ports map
	h.ports = Ports{}

	// Loop until Stop()
	for {
		select {

		// Broadcast
		case msg := <-h.broadcast:
			var recipients = h.ports

			// OnHubBroadcaster event trigger
			if t, ok := trigger.(OnHubBroadcaster); ok {
				ports, err := t.OnHubBroadcast(msg)
				if err != nil {
					continue
				} else if ports != nil {
					recipients = ports
				}
			}

			// Send message to the recipients
			for conn, _ := range recipients {
				if msg.From == conn {
					// Skip original connector
					continue
				} else if err := conn.Send(msg); err != nil {
					conn.Close(err)
					delete(h.ports, conn)
					// OnHubLeaver event trigger
					if t, ok := trigger.(OnHubLeaver); ok {
						t.OnHubLeave(conn)
					}
				}
			}

		// Join
		case conn := <-h.join:
			// Throttle
			if throttler != nil {
				<-h.timeslots
			}

			// OnHubJoiner event trigger
			if t, ok := trigger.(OnHubJoiner); ok {
				if t.OnHubJoin(conn) != nil {
					continue
				}
			}

			h.ports[conn] = true

		// Leave
		case conn := <-h.leave:
			delete(h.ports, conn)
			// OnHubLeaver event trigger
			if t, ok := trigger.(OnHubLeaver); ok {
				t.OnHubLeave(conn)
			}

		// Stop
		case <-h.stop:
			// OnHubCloser event trigger
			if t, ok := trigger.(OnHubCloser); ok {
				t.OnHubClose(h.ports)
			}
			return nil

		}
	}
	return ErrShouldNotReachThis
}
<commit_msg>Bugfix release 2.0.1<commit_after>// Copyright 2015 The Pivo Authors. All rights reserved.
// Use of this source code is governed by a Simplified BSD
// license that can be found in the LICENSE file.

package pivo

import (
	"errors"
	"net"
	"time"
)

// The Pivo package version numbers
const Version = "2.0.1"

// Default value for the join rate limit.
// Currently defined as a maximum of 64 per seconds.
const DefaultJoinLimitRateInterval = time.Second / 64

// Default value for the allowed burst over the join limit.
// Currently defined as a maximum of 32 more connections.
const DefaultJoinLimitRateBurst = 32

// Default value for the size of the join queue.
// Currently defined as 256 pending connections before new
// ones are discarded.
const DefaultJoinMaxQueueSize = 256

// Marker for binary message type
const IsBinaryMessage = 1

// Marker for text message type
const IsTextMessage = 0

// Error is thrown when the join queue is full.
var ErrJoinQueueIsFull = errors.New("join queue is full")

// Error is thrown when a connector has its port buffer full.
var ErrPortBufferIsFull = errors.New("port buffer is full")

// Error is thrown when unexpected code path is reached.
var ErrShouldNotReachThis = errors.New("should not reach this")

// Connector is the interface that wraps the basic methods needed
// to use a connector with the hub.
type Connector interface {
	Close(error) error
	Protocol() string
	RemoteAddr() net.Addr
	Send(*Message) error
}

// OnBinaryReader is called when binary data is read from
// a connector.
type OnBinaryReader interface {
	OnBinaryRead([]byte) error
}

// OnBinaryReadCloser is the interface that wraps basic methods
// both for reading binary data and getting notified about a
// closed connection.
type OnBinaryReadCloser interface {
	OnBinaryReader
	OnCloser
}

// OnCloser is the interface that requires a method to call upon
// disconnection of a connector.
type OnCloser interface {
	OnClose(error) error
}

// OnReader is the interface that wraps the methods called when
// data is read from a connector.
type OnReader interface {
	OnBinaryReader
	OnTextReader
}

// OnReadCloser wraps the basic methods needed to be notified
// when data has been read or the connector has closed.
type OnReadCloser interface {
	OnCloser
	OnReader
}

// OnTextReader is called when text data is read from
// a connector.
type OnTextReader interface {
	OnTextRead(string) error
}

// OnTextReadCloser is the interface that wraps basic methods
// both for reading text data and getting notified about a
// closed connection.
type OnTextReadCloser interface {
	OnTextReader
	OnCloser
}

// A Hub is a collection of connectors with some specified settings.
type Hub struct {
	// The interval at which new connections are processed.
	// A Duration of time.Second / 100 would mean hundred
	// of new connections are processed in a second.
	JoinLimitRateInterval time.Duration

	// The size of the burst allowed when the join rate limit
	// is reached. A value of 10 would mean that 10 extra
	// connections may wait to join the hub.
	JoinLimitRateBurst uint

	// The maximum number of new connections waiting to
	// join the hub before ErrJoinQueueIsFull is being
	// thrown.
	JoinMaxQueueSize uint

	broadcast Port           // Broadcast channel
	join      chan Connector // Join the connector
	leave     chan Connector // Disjoin the connector
	ports     Ports          // Map of connected connectors
	stop      chan bool      // Stop the hub
	timeslots chan bool      // Join burst timeslots
}

// OnHubBroadcaster is triggered before the message is delivered.
// Callee may alter the message in any way as well as
// provide a different list of recipients ports.
// If error is thrown, broadcast will not occur at all.
type OnHubBroadcaster interface {
	OnHubBroadcast(*Message) (Ports, error)
}

// OnHubJoiner is triggered before a connector may join the hub.
// If error is thrown, join will not occur at all.
type OnHubJoiner interface {
	OnHubJoin(Connector) error
}

// OnHubLeaver is triggered before a connector disjoin the hub.
// Nothing may prevent this from happening at this stage.
type OnHubLeaver interface {
	OnHubLeave(Connector)
}

// OnHubCloser is triggered right before the hub stops.
// The map of connected connectors is provided and is
// guaranteed to be accurate at the time the callee has it.
type OnHubCloser interface {
	OnHubClose(Ports)
}

// Message is a data structure with bytes of data, type of data,
// and its original connector.
type Message struct {
	Data []byte    // Bytes of data
	From Connector // Original connector
	Type int       // Type of data
}

// Port is a channel transporting a pointer to a Message.
type Port chan *Message

// Ports is a map of Connectors that have joined the hub.
type Ports map[Connector]bool

// throttler is the timeslots ticker.
type throttler struct {
	stop chan bool // Stop the throttler
}

// BinaryMessage formats a binary message and returns
// a pointer to it.
func BinaryMessage(from Connector, bin []byte) *Message {
	return &Message{bin, from, IsBinaryMessage}
}

// DefaultHub instantiate a new hub with default settings.
func DefaultHub() *Hub {
	return &Hub{
		JoinLimitRateBurst:    DefaultJoinLimitRateBurst,
		JoinLimitRateInterval: DefaultJoinLimitRateInterval,
		JoinMaxQueueSize:      DefaultJoinMaxQueueSize,
	}
}

// TextMessage formats a text message and returns
// a pointer to it.
func TextMessage(from Connector, text string) *Message {
	return &Message{[]byte(text), from, IsTextMessage}
}

// throttler fills the timeslots channel at specified interval.
func (h *Hub) throttler(rate time.Duration) *throttler {
	throttler := &throttler{stop: make(chan bool)}
	go func() {
		h.timeslots = make(chan bool, h.JoinLimitRateBurst+1)
		ticker := time.NewTicker(rate)
		defer func() { ticker.Stop(); close(h.timeslots) }()
		for {
			select {
			case <-throttler.stop:
				return
			case <-ticker.C:
				select {
				case h.timeslots <- true:
				default:
				}
			}
		}
	}()
	return throttler
}

// Broadcast sends a message to all connectors on the hub.
func (h *Hub) Broadcast(m *Message) { h.broadcast <- m }

// Leave disjoins the given connector from the hub.
func (h *Hub) Leave(c Connector) { h.leave <- c }

// Stop brings the hub down.
func (h *Hub) Stop() { h.stop <- true }

// Join adds the given connector to the hub.
func (h *Hub) Join(c Connector) error {
	select {
	case h.join <- c:
	default:
		return ErrJoinQueueIsFull
	}
	return nil
}

// StartAndServe serves the hub until Stop() is requested.
func (h *Hub) StartAndServe(trigger interface{}) error {
	var throttler *throttler

	// Start a throttler if necessary
	if h.JoinLimitRateInterval > 0 {
		throttler = h.throttler(h.JoinLimitRateInterval)
		defer func() { throttler.stop <- true }()
	}

	// Initialize control channels
	h.broadcast = make(Port)
	h.join = make(chan Connector, h.JoinMaxQueueSize)
	h.leave = make(chan Connector)
	h.stop = make(chan bool)

	// Initialize ports map
	h.ports = Ports{}

	// Loop until Stop()
	for {
		select {

		// Broadcast
		case msg := <-h.broadcast:
			var recipients = h.ports

			// OnHubBroadcaster event trigger
			if t, ok := trigger.(OnHubBroadcaster); ok {
				ports, err := t.OnHubBroadcast(msg)
				if err != nil {
					continue
				} else if ports != nil {
					recipients = ports
				}
			}

			// Send message to the recipients
			for conn, _ := range recipients {
				if msg.From == conn {
					// Skip original connector
					continue
				} else if err := conn.Send(msg); err != nil {
					conn.Close(err)
					delete(h.ports, conn)
					// OnHubLeaver event trigger
					if t, ok := trigger.(OnHubLeaver); ok {
						t.OnHubLeave(conn)
					}
				}
			}

		// Join
		case conn := <-h.join:
			// Throttle
			if throttler != nil {
				<-h.timeslots
			}

			// OnHubJoiner event trigger
			if t, ok := trigger.(OnHubJoiner); ok {
				if t.OnHubJoin(conn) != nil {
					continue
				}
			}

			h.ports[conn] = true

		// Leave
		case conn := <-h.leave:
			delete(h.ports, conn)
			// OnHubLeaver event trigger
			if t, ok := trigger.(OnHubLeaver); ok {
				t.OnHubLeave(conn)
			}

		// Stop
		case <-h.stop:
			// OnHubCloser event trigger
			if t, ok := trigger.(OnHubCloser); ok {
				t.OnHubClose(h.ports)
			}
			return nil

		}
	}
	return ErrShouldNotReachThis
}
<|endoftext|>


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

package mixins

import (
	"github.com/google/gxui"
	"github.com/google/gxui/math"
	"github.com/google/gxui/mixins/outer"
	"github.com/google/gxui/mixins/parts"
)

type WindowOuter interface {
	gxui.Window
	outer.Attachable
	outer.Bounds
	outer.IsVisibler
	outer.LayoutChildren
	outer.PaintChilder
	outer.Painter
	outer.Parenter
}

type Window struct {
	parts.Attachable
	parts.Container
	parts.Paddable
	parts.PaintChildren

	driver             gxui.Driver
	outer              WindowOuter
	viewport           gxui.Viewport
	mouseController    *gxui.MouseController
	keyboardController *gxui.KeyboardController
	focusController    *gxui.FocusController
	layoutPending      bool
	drawPending        bool
	updatePending      bool
	onAttach           gxui.Event
	onDetach           gxui.Event
}

func (w *Window) requestUpdate() {
	if !w.updatePending {
		w.updatePending = true
		w.driver.Call(w.update)
	}
}

func (w *Window) update() {
	if !w.Attached() {
		// Window was detached between requestUpdate() and update()
		w.updatePending = false
		w.layoutPending = false
		w.drawPending = false
		return
	}
	w.updatePending = false
	if w.layoutPending {
		w.layoutPending = false
		w.drawPending = true
		w.outer.LayoutChildren()
	}
	if w.drawPending {
		w.drawPending = false
		w.Draw()
	}
}

func (w *Window) Init(outer WindowOuter, driver gxui.Driver, width, height int, title string) {
	w.Attachable.Init(outer)
	w.Container.Init(outer)
	w.Paddable.Init(outer)
	w.PaintChildren.Init(outer)
	w.outer = outer
	w.driver = driver
	w.viewport = driver.CreateViewport(width, height, title)
	w.focusController = gxui.CreateFocusController(outer)
	w.mouseController = gxui.CreateMouseController(outer, w.focusController)
	w.keyboardController = gxui.CreateKeyboardController(outer)
	w.viewport.OnResize(func() {
		w.outer.LayoutChildren()
		w.Draw()
	})

	// Window starts shown
	w.Attach()

	// Interface compliance test
	_ = gxui.Window(w)
}

func (w *Window) Draw() gxui.Canvas {
	c := w.driver.CreateCanvas(w.viewport.SizeDips())
	w.outer.Paint(c)
	c.Complete()
	w.viewport.SetCanvas(c)
	c.Release()
	return c
}

func (w *Window) LayoutChildren() {
	s := w.Bounds().Size().Contract(w.Padding())
	o := w.Padding().LT()
	for _, c := range w.outer.Children() {
		c.Layout(c.DesiredSize(math.ZeroSize, s).Rect().Offset(o))
	}
}

func (w *Window) Bounds() math.Rect {
	s := w.viewport.SizeDips()
	return math.CreateRect(0, 0, s.W, s.H)
}

func (w *Window) Parent() gxui.Container {
	return nil
}

func (w *Window) Viewport() gxui.Viewport {
	return w.viewport
}

func (w *Window) Title() string {
	return w.viewport.Title()
}

func (w *Window) SetTitle(t string) {
	w.viewport.SetTitle(t)
}

func (w *Window) Scale() float32 {
	return w.viewport.Scale()
}

func (w *Window) SetScale(scale float32) {
	w.viewport.SetScale(scale)
}

func (w *Window) Show() {
	w.Attach()
	w.viewport.Show()
}

func (w *Window) Hide() {
	w.Detach()
	w.viewport.Hide()
}

func (w *Window) Close() {
	w.Detach()
	w.viewport.Close()
}

func (w *Window) Focus() gxui.Focusable {
	return w.focusController.Focus()
}

func (w *Window) SetFocus(c gxui.Control) bool {
	fc := w.focusController
	if c == nil {
		fc.SetFocus(nil)
		return true
	}
	if f := fc.Focusable(c); f != nil {
		fc.SetFocus(f)
		return true
	}
	return false
}

func (w *Window) IsVisible() bool {
	return true
}

func (w *Window) OnClose(f func()) gxui.EventSubscription {
	return w.viewport.OnClose(f)
}

func (w *Window) OnResize(f func()) gxui.EventSubscription {
	return w.viewport.OnResize(f)
}

func (w *Window) OnMouseMove(f func(gxui.MouseEvent)) gxui.EventSubscription {
	return w.viewport.OnMouseMove(func(ev gxui.MouseEvent) {
		ev.Window = w
		ev.WindowPoint = ev.Point
		f(ev)
	})
}

func (w *Window) OnMouseEnter(f func(gxui.MouseEvent)) gxui.EventSubscription {
	return w.viewport.OnMouseEnter(func(ev gxui.MouseEvent) {
		ev.Window = w
		ev.WindowPoint = ev.Point
		f(ev)
	})
}

func (w *Window) OnMouseExit(f func(gxui.MouseEvent)) gxui.EventSubscription {
	return w.viewport.OnMouseExit(func(ev gxui.MouseEvent) {
		ev.Window = w
		ev.WindowPoint = ev.Point
		f(ev)
	})
}

func (w *Window) OnMouseDown(f func(gxui.MouseEvent)) gxui.EventSubscription {
	return w.viewport.OnMouseDown(func(ev gxui.MouseEvent) {
		ev.Window = w
		ev.WindowPoint = ev.Point
		f(ev)
	})
}

func (w *Window) OnMouseUp(f func(gxui.MouseEvent)) gxui.EventSubscription {
	return w.viewport.OnMouseUp(func(ev gxui.MouseEvent) {
		ev.Window = w
		ev.WindowPoint = ev.Point
		f(ev)
	})
}

func (w *Window) OnMouseScroll(f func(gxui.MouseEvent)) gxui.EventSubscription {
	return w.viewport.OnMouseScroll(func(ev gxui.MouseEvent) {
		ev.Window = w
		ev.WindowPoint = ev.Point
		f(ev)
	})
}

func (w *Window) OnKeyDown(f func(gxui.KeyboardEvent)) gxui.EventSubscription {
	return w.viewport.OnKeyDown(f)
}

func (w *Window) OnKeyUp(f func(gxui.KeyboardEvent)) gxui.EventSubscription {
	return w.viewport.OnKeyUp(f)
}

func (w *Window) OnKeyRepeat(f func(gxui.KeyboardEvent)) gxui.EventSubscription {
	return w.viewport.OnKeyRepeat(f)
}

func (w *Window) OnKeyStroke(f func(gxui.KeyStrokeEvent)) gxui.EventSubscription {
	return w.viewport.OnKeyStroke(f)
}

func (w *Window) Relayout() {
	w.layoutPending = true
	w.requestUpdate()
}

func (w *Window) Redraw() {
	w.drawPending = true
	w.requestUpdate()
}

func (w *Window) Click(gxui.MouseEvent)       {}
func (w *Window) DoubleClick(gxui.MouseEvent) {}

func (w *Window) KeyPress(ev gxui.KeyboardEvent) {
	if ev.Key == gxui.KeyTab {
		if ev.Modifier&gxui.ModShift != 0 {
			w.focusController.FocusPrev()
		} else {
			w.focusController.FocusNext()
		}
	}
}
func (w *Window) KeyStroke(gxui.KeyStrokeEvent) {}
<commit_msg>Don't attempt to create a zero-size canvas when the window is minimized.<commit_after>// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package mixins

import (
	"github.com/google/gxui"
	"github.com/google/gxui/math"
	"github.com/google/gxui/mixins/outer"
	"github.com/google/gxui/mixins/parts"
)

type WindowOuter interface {
	gxui.Window
	outer.Attachable
	outer.Bounds
	outer.IsVisibler
	outer.LayoutChildren
	outer.PaintChilder
	outer.Painter
	outer.Parenter
}

type Window struct {
	parts.Attachable
	parts.Container
	parts.Paddable
	parts.PaintChildren

	driver             gxui.Driver
	outer              WindowOuter
	viewport           gxui.Viewport
	mouseController    *gxui.MouseController
	keyboardController *gxui.KeyboardController
	focusController    *gxui.FocusController
	layoutPending      bool
	drawPending        bool
	updatePending      bool
	onAttach           gxui.Event
	onDetach           gxui.Event
}

func (w *Window) requestUpdate() {
	if !w.updatePending {
		w.updatePending = true
		w.driver.Call(w.update)
	}
}

func (w *Window) update() {
	if !w.Attached() {
		// Window was detached between requestUpdate() and update()
		w.updatePending = false
		w.layoutPending = false
		w.drawPending = false
		return
	}
	w.updatePending = false
	if w.layoutPending {
		w.layoutPending = false
		w.drawPending = true
		w.outer.LayoutChildren()
	}
	if w.drawPending {
		w.drawPending = false
		w.Draw()
	}
}

func (w *Window) Init(outer WindowOuter, driver gxui.Driver, width, height int, title string) {
	w.Attachable.Init(outer)
	w.Container.Init(outer)
	w.Paddable.Init(outer)
	w.PaintChildren.Init(outer)
	w.outer = outer
	w.driver = driver
	w.viewport = driver.CreateViewport(width, height, title)
	w.focusController = gxui.CreateFocusController(outer)
	w.mouseController = gxui.CreateMouseController(outer, w.focusController)
	w.keyboardController = gxui.CreateKeyboardController(outer)
	w.viewport.OnResize(func() {
		w.outer.LayoutChildren()
		w.Draw()
	})

	// Window starts shown
	w.Attach()

	// Interface compliance test
	_ = gxui.Window(w)
}

func (w *Window) Draw() gxui.Canvas {
	if s := w.viewport.SizeDips(); s != math.ZeroSize {
		c := w.driver.CreateCanvas(w.viewport.SizeDips())
		w.outer.Paint(c)
		c.Complete()
		w.viewport.SetCanvas(c)
		c.Release()
		return c
	} else {
		return nil
	}
}

func (w *Window) LayoutChildren() {
	s := w.Bounds().Size().Contract(w.Padding())
	o := w.Padding().LT()
	for _, c := range w.outer.Children() {
		c.Layout(c.DesiredSize(math.ZeroSize, s).Rect().Offset(o))
	}
}

func (w *Window) Bounds() math.Rect {
	s := w.viewport.SizeDips()
	return math.CreateRect(0, 0, s.W, s.H)
}

func (w *Window) Parent() gxui.Container {
	return nil
}

func (w *Window) Viewport() gxui.Viewport {
	return w.viewport
}

func (w *Window) Title() string {
	return w.viewport.Title()
}

func (w *Window) SetTitle(t string) {
	w.viewport.SetTitle(t)
}

func (w *Window) Scale() float32 {
	return w.viewport.Scale()
}

func (w *Window) SetScale(scale float32) {
	w.viewport.SetScale(scale)
}

func (w *Window) Show() {
	w.Attach()
	w.viewport.Show()
}

func (w *Window) Hide() {
	w.Detach()
	w.viewport.Hide()
}

func (w *Window) Close() {
	w.Detach()
	w.viewport.Close()
}

func (w *Window) Focus() gxui.Focusable {
	return w.focusController.Focus()
}

func (w *Window) SetFocus(c gxui.Control) bool {
	fc := w.focusController
	if c == nil {
		fc.SetFocus(nil)
		return true
	}
	if f := fc.Focusable(c); f != nil {
		fc.SetFocus(f)
		return true
	}
	return false
}

func (w *Window) IsVisible() bool {
	return true
}

func (w *Window) OnClose(f func()) gxui.EventSubscription {
	return w.viewport.OnClose(f)
}

func (w *Window) OnResize(f func()) gxui.EventSubscription {
	return w.viewport.OnResize(f)
}

func (w *Window) OnMouseMove(f func(gxui.MouseEvent)) gxui.EventSubscription {
	return w.viewport.OnMouseMove(func(ev gxui.MouseEvent) {
		ev.Window = w
		ev.WindowPoint = ev.Point
		f(ev)
	})
}

func (w *Window) OnMouseEnter(f func(gxui.MouseEvent)) gxui.EventSubscription {
	return w.viewport.OnMouseEnter(func(ev gxui.MouseEvent) {
		ev.Window = w
		ev.WindowPoint = ev.Point
		f(ev)
	})
}

func (w *Window) OnMouseExit(f func(gxui.MouseEvent)) gxui.EventSubscription {
	return w.viewport.OnMouseExit(func(ev gxui.MouseEvent) {
		ev.Window = w
		ev.WindowPoint = ev.Point
		f(ev)
	})
}

func (w *Window) OnMouseDown(f func(gxui.MouseEvent)) gxui.EventSubscription {
	return w.viewport.OnMouseDown(func(ev gxui.MouseEvent) {
		ev.Window = w
		ev.WindowPoint = ev.Point
		f(ev)
	})
}

func (w *Window) OnMouseUp(f func(gxui.MouseEvent)) gxui.EventSubscription {
	return w.viewport.OnMouseUp(func(ev gxui.MouseEvent) {
		ev.Window = w
		ev.WindowPoint = ev.Point
		f(ev)
	})
}

func (w *Window) OnMouseScroll(f func(gxui.MouseEvent)) gxui.EventSubscription {
	return w.viewport.OnMouseScroll(func(ev gxui.MouseEvent) {
		ev.Window = w
		ev.WindowPoint = ev.Point
		f(ev)
	})
}

func (w *Window) OnKeyDown(f func(gxui.KeyboardEvent)) gxui.EventSubscription {
	return w.viewport.OnKeyDown(f)
}

func (w *Window) OnKeyUp(f func(gxui.KeyboardEvent)) gxui.EventSubscription {
	return w.viewport.OnKeyUp(f)
}

func (w *Window) OnKeyRepeat(f func(gxui.KeyboardEvent)) gxui.EventSubscription {
	return w.viewport.OnKeyRepeat(f)
}

func (w *Window) OnKeyStroke(f func(gxui.KeyStrokeEvent)) gxui.EventSubscription {
	return w.viewport.OnKeyStroke(f)
}

func (w *Window) Relayout() {
	w.layoutPending = true
	w.requestUpdate()
}

func (w *Window) Redraw() {
	w.drawPending = true
	w.requestUpdate()
}

func (w *Window) Click(gxui.MouseEvent)       {}
func (w *Window) DoubleClick(gxui.MouseEvent) {}

func (w *Window) KeyPress(ev gxui.KeyboardEvent) {
	if ev.Key == gxui.KeyTab {
		if ev.Modifier&gxui.ModShift != 0 {
			w.focusController.FocusPrev()
		} else {
			w.focusController.FocusNext()
		}
	}
}
func (w *Window) KeyStroke(gxui.KeyStrokeEvent) {}
<|endoftext|>


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

package keyserver

import (
	"bytes"
	"encoding/base64"
	"fmt"
	"log"
	"math"
	"strings"

	"github.com/yahoo/coname"
	"github.com/yahoo/coname/keyserver/dkim"
	"github.com/yahoo/coname/keyserver/replication"
	"github.com/yahoo/coname/proto"
	"github.com/yahoo/coname/vrf"
	"golang.org/x/crypto/sha3"
	"golang.org/x/net/context"
)

func (ks *Keyserver) verifyUpdateDeterministic(prevUpdate *proto.UpdateRequest, req *proto.UpdateRequest) error {
	var prevEntry *proto.Entry
	if prevUpdate != nil {
		prevEntry = &prevUpdate.Update.NewEntry.Entry
	}
	if err := coname.VerifyUpdate(prevEntry, req.Update); err != nil {
		return err
	}
	return nil
}

func (ks *Keyserver) verifyUpdateEdge(req *proto.UpdateRequest) error {
	prevUpdate, err := ks.getUpdate(req.Update.NewEntry.Index, math.MaxUint64)
	if err != nil {
		log.Print(err)
		return fmt.Errorf("internal error")
	}
	if prevUpdate == nil { // registration: check email proof
		if !ks.insecureSkipEmailProof {
			email, payload, err := dkim.CheckEmailProof(req.DKIMProof, ks.emailProofToAddr,
				ks.emailProofSubjectPrefix, ks.lookupTXT, ks.clk.Now)
			if err != nil {
				return err
			}
			if got, want := email, req.LookupParameters.UserId; got != want {
				return fmt.Errorf("requested user ID does not match the email proof: %q != %q", got, want)
			}
			lastAtIndex := strings.LastIndex(req.LookupParameters.UserId, "@")
			if lastAtIndex == -1 {
				return fmt.Errorf("requested user id is not a valid email address: %q", req.LookupParameters.UserId)
			}
			if _, ok := ks.emailProofAllowedDomains[req.LookupParameters.UserId[:lastAtIndex]]; !ok {
				return fmt.Errorf("domain not in registration whitelist: %q", req.LookupParameters.UserId[:lastAtIndex])
			}
			entryHash, err := base64.StdEncoding.DecodeString(payload)
			if err != nil {
				return fmt.Errorf("bad base64 in email proof: %q", payload)
			}
			var entryHashProposed [32]byte
			sha3.ShakeSum256(entryHashProposed[:], req.Update.NewEntry.Encoding)
			if !bytes.Equal(entryHashProposed[:], entryHash[:]) {
				return fmt.Errorf("email proof does not match requested entry")
			}
		}
	}

	return ks.verifyUpdateDeterministic(prevUpdate, req)
}

type updateOutput struct {
	Epoch uint64 // which epoch the update will appear in
	Error error
}

// Update implements proto.E2EKS.UpdateServer
func (ks *Keyserver) Update(ctx context.Context, req *proto.UpdateRequest) (*proto.LookupProof, error) {
	ctx, _ = context.WithTimeout(ctx, ks.clientTimeout)
	req.Update.NewEntry.Index = vrf.Compute([]byte(req.LookupParameters.UserId), ks.vrfSecret)
	req.Update.NewEntry.UpdateEncoding()
	if err := ks.verifyUpdateEdge(req); err != nil {
		return nil, err
	}

	uid := genUID()
	ch := ks.wr.Wait(uid)
	ks.log.Propose(ctx, replication.LogEntry{Data: proto.MustMarshal(&proto.KeyserverStep{
		UID:    uid,
		Update: req,
	})})
	select {
	case <-ctx.Done():
		ks.wr.Notify(uid, nil)
		return nil, ctx.Err()
	case v := <-ch:
		out := v.(updateOutput)
		if out.Error != nil {
			return nil, out.Error
		}
		return ks.blockingLookup(ctx, req.LookupParameters, out.Epoch)
	}
}
<commit_msg>server: better errors for dkim proofs<commit_after>// Copyright 2014-2015 The Dename Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// 	http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.

package keyserver

import (
	"bytes"
	"encoding/base64"
	"fmt"
	"log"
	"math"
	"strings"

	"github.com/yahoo/coname"
	"github.com/yahoo/coname/keyserver/dkim"
	"github.com/yahoo/coname/keyserver/replication"
	"github.com/yahoo/coname/proto"
	"github.com/yahoo/coname/vrf"
	"golang.org/x/crypto/sha3"
	"golang.org/x/net/context"
)

func (ks *Keyserver) verifyUpdateDeterministic(prevUpdate *proto.UpdateRequest, req *proto.UpdateRequest) error {
	var prevEntry *proto.Entry
	if prevUpdate != nil {
		prevEntry = &prevUpdate.Update.NewEntry.Entry
	}
	if err := coname.VerifyUpdate(prevEntry, req.Update); err != nil {
		return err
	}
	return nil
}

func (ks *Keyserver) verifyUpdateEdge(req *proto.UpdateRequest) error {
	prevUpdate, err := ks.getUpdate(req.Update.NewEntry.Index, math.MaxUint64)
	if err != nil {
		log.Print(err)
		return fmt.Errorf("internal error")
	}
	if prevUpdate == nil { // registration: check email proof
		if !ks.insecureSkipEmailProof {
			email, payload, err := dkim.CheckEmailProof(req.DKIMProof, ks.emailProofToAddr,
				ks.emailProofSubjectPrefix, ks.lookupTXT, ks.clk.Now)
			if err != nil {
				return fmt.Errorf("failed to verify DKIM proof: %s", err)
			}
			if got, want := email, req.LookupParameters.UserId; got != want {
				return fmt.Errorf("requested user ID does not match the email proof: %q != %q", got, want)
			}
			lastAtIndex := strings.LastIndex(req.LookupParameters.UserId, "@")
			if lastAtIndex == -1 {
				return fmt.Errorf("requested user id is not a valid email address: %q", req.LookupParameters.UserId)
			}
			if _, ok := ks.emailProofAllowedDomains[req.LookupParameters.UserId[:lastAtIndex]]; !ok {
				return fmt.Errorf("domain not in registration whitelist: %q", req.LookupParameters.UserId[:lastAtIndex])
			}
			entryHash, err := base64.StdEncoding.DecodeString(payload)
			if err != nil {
				return fmt.Errorf("bad base64 in email proof: %q", payload)
			}
			var entryHashProposed [32]byte
			sha3.ShakeSum256(entryHashProposed[:], req.Update.NewEntry.Encoding)
			if !bytes.Equal(entryHashProposed[:], entryHash[:]) {
				return fmt.Errorf("email proof does not match requested entry")
			}
		}
	}

	return ks.verifyUpdateDeterministic(prevUpdate, req)
}

type updateOutput struct {
	Epoch uint64 // which epoch the update will appear in
	Error error
}

// Update implements proto.E2EKS.UpdateServer
func (ks *Keyserver) Update(ctx context.Context, req *proto.UpdateRequest) (*proto.LookupProof, error) {
	ctx, _ = context.WithTimeout(ctx, ks.clientTimeout)
	req.Update.NewEntry.Index = vrf.Compute([]byte(req.LookupParameters.UserId), ks.vrfSecret)
	req.Update.NewEntry.UpdateEncoding()
	if err := ks.verifyUpdateEdge(req); err != nil {
		return nil, err
	}

	uid := genUID()
	ch := ks.wr.Wait(uid)
	ks.log.Propose(ctx, replication.LogEntry{Data: proto.MustMarshal(&proto.KeyserverStep{
		UID:    uid,
		Update: req,
	})})
	select {
	case <-ctx.Done():
		ks.wr.Notify(uid, nil)
		return nil, ctx.Err()
	case v := <-ch:
		out := v.(updateOutput)
		if out.Error != nil {
			return nil, out.Error
		}
		return ks.blockingLookup(ctx, req.LookupParameters, out.Epoch)
	}
}
<|endoftext|>


<commit_before>// Library functions for working with HUE
package hue

import  (
  "fmt"
  "net/http"
  "encoding/json"
  "io"
)

const meethue = "http://www.meethue.com/api/nupnp"
const developer_name = "newdeveloper"

func ConvertJsonToMap(b io.Reader) (m map[string] interface{}, ok bool) {
  dec := json.NewDecoder(b)
  var f interface{}
  dec.Decode(&f)
  //Its possible we got an array rather than a simple map
  //In this case, take the first element
  switch v := f.(type) {
    case []interface{}:
      m, ok = v[0].(map[string]interface{})
    default:
      m, ok = v.(map[string] interface{})
  }
  return
}

func GetHueIp() string {
  resp, err := http.Get(meethue)
  if (err != nil) {
    fmt.Println("Error %v", err)
  }
  ms, ok := ConvertJsonToMap(resp.Body)
  if (!ok) {
    fmt.Println("Could not process the response")
  }

  ip := ms["internalipaddress"].(string)
  return ip
}

//func IssueGetRequest(request_uri string) map[string] interface{} {
//}

<commit_msg>created struct for status message<commit_after>// Library functions for working with HUE
package hue

import  (
  "fmt"
  "net/http"
  "encoding/json"
  "io"
)

type MsgWrapper struct {
  msg StatusMessage
}

type StatusMessage struct {
  Config map[string] interface{}
  Lights map[string] LightMessage
  Groups map[string] interface{}
  Schedules map[string] interface{}
  Scenes  map[string] interface{}
}

type LightState struct {
  On bool
  Bri int
  Hue int
  Sat int
  Xy []float64
  Alert string
  Effect string
  Colormode string
  Reachable bool
}

type LightMessage struct {
  State LightState
  Name string
}

type LightsDict struct {
  dict map[string] interface{}
}

type Lights interface{}
type Groups interface{}
type Config interface{}
type Schedules interface{}

const meethue = "http://www.meethue.com/api/nupnp"
const developer_name = "newdeveloper"

func DoPut(url, data string) {
  req, err := http.NewRequest("PUT", url, data)
  if err != nil {
    fmt.Printf("Error: %v\n", err)
  }
  client := http.Client{}
  resp, err := client.Do(req)
  fmt.Printf("Response: %v\n", resp)

}

func GetStatusMsg() string {
  resp, err := http.Get("http://10.0.1.4/api/newdeveloper")
  if err != nil {
    fmt.Println("Error: %v", err)
  }
  fmt.Println("%v", resp)
  dec := json.NewDecoder(resp.Body)
  var m StatusMessage
  err = dec.Decode(&m)
  if err != nil {
    fmt.Println("Error: %v", err)
  }
  fmt.Printf("%v\n", m.Lights["1"])
  resp, err = DoPut("http://10.0.1.4/api/lights/1/state", "{ \"on\":false }")
  if err != nil {
    fmt.Println("Error: %v", err)
  }
  fmt.Println(resp)
  return "success"
}

func ConvertJsonToMap(b io.Reader) (m map[string] interface{}, ok bool) {
  dec := json.NewDecoder(b)
  var f interface{}
  dec.Decode(&f)
  //Its possible we got an array rather than a simple map
  //In this case, take the first element
  switch v := f.(type) {
    case []interface{}:
      m, ok = v[0].(map[string]interface{})
    default:
      m, ok = v.(map[string] interface{})
  }
  return
}

func GetHueIp() string {
  resp, err := http.Get(meethue)
  if (err != nil) {
    fmt.Println("Error %v", err)
  }
  ms, ok := ConvertJsonToMap(resp.Body)
  if (!ok) {
    fmt.Println("Could not process the response")
  }

  ip := ms["internalipaddress"].(string)
  return ip
}

//func IssueGetRequest(request_uri string) map[string] interface{} {
//}

<|endoftext|>


<commit_before>package bbio

// #include <stddef.h>
// #include <sys/types.h>
// #include <linux/i2c-dev.h>
import "C"

import (
	"fmt"
	"os"
	"syscall"
)

// #include <linux/i2c.h>

type I2C struct {
	address int
	file    *os.File
}

func NewI2C(address int) (*I2C, error) {
	filename := fmt.Sprintf("/dev/i2c-%d", address)
	file, err := os.OpenFile(filename, os.O_RDWR, 0)
	if err != nil {
		return nil, err
	}

	_, _, err = syscall.Syscall(syscall.SYS_IOCTL, file.Fd(), C.I2C_SLAVE, uintptr(address))
	if err != nil {
		return nil, err
	}

	return &I2C{address, file}, nil
}

func (i2c *I2C) errMsg() error {
	return fmt.Errorf("Error accessing 0x%02X: Check your I2C address", i2c.address)
}

func (i2c *I2C) ReadUint8() (uint8, error) {
	result := C.i2c_smbus_read_byte(C.int(i2c.file.Fd()))
	if result == -1 {
		return 0, i2c.errMsg()
	}
	return uint8(result), nil
}

func (i2c *I2C) WriteUint8(value uint8) error {
	result := C.i2c_smbus_write_byte(C.int(i2c.file.Fd()), C.__u8(value))
	if result == -1 {
		return i2c.errMsg()
	}
	return nil
}

func (i2c *I2C) ReadInt8() (int8, error) {
	result := C.i2c_smbus_read_byte(C.int(i2c.file.Fd()))
	if result == -1 {
		return 0, i2c.errMsg()
	}
	if result >= 1<<7 {
		result -= 1 << 8
	}
	return int8(result), nil
}

func (i2c *I2C) WriteInt8(value int8) error {
	result := C.i2c_smbus_write_byte(C.int(i2c.file.Fd()), C.__u8(value))
	if result == -1 {
		return i2c.errMsg()
	}
	return nil
}

func (i2c *I2C) ReadUint8Cmd(command uint8) (uint8, error) {
	result := C.i2c_smbus_read_byte_data(C.int(i2c.file.Fd()), C.__u8(command))
	if result == -1 {
		return 0, i2c.errMsg()
	}
	return uint8(result), nil
}

func (i2c *I2C) WriteUint8Cmd(command uint8, value uint8) error {
	result := C.i2c_smbus_write_byte_data(C.int(i2c.file.Fd()), C.__u8(command), C.__u8(value))
	if result == -1 {
		return i2c.errMsg()
	}
	return nil
}

func (i2c *I2C) ReadInt8Cmd(command uint8) (int8, error) {
	result := C.i2c_smbus_read_byte_data(C.int(i2c.file.Fd()), C.__u8(command))
	if result == -1 {
		return 0, i2c.errMsg()
	}
	if result >= 1<<7 {
		result -= 1 << 8
	}
	return int8(result), nil
}

func (i2c *I2C) WriteInt8Cmd(command uint8, value int8) error {
	result := C.i2c_smbus_write_byte_data(C.int(i2c.file.Fd()), C.__u8(command), C.__u8(value))
	if result == -1 {
		return i2c.errMsg()
	}
	return nil
}

func (i2c *I2C) ReadUint16Cmd(command uint8) (uint16, error) {
	result := C.i2c_smbus_read_word_data(C.int(i2c.file.Fd()), C.__u8(command))
	if result == -1 {
		return 0, i2c.errMsg()
	}
	return uint16(result), nil
}

func (i2c *I2C) WriteUint16Cmd(command uint8, value uint16) error {
	result := C.i2c_smbus_write_word_data(C.int(i2c.file.Fd()), C.__u8(command), C.__u16(value))
	if result == -1 {
		return i2c.errMsg()
	}
	return nil
}

func (i2c *I2C) ReadInt16Cmd(command uint8) (int16, error) {
	result := C.i2c_smbus_read_word_data(C.int(i2c.file.Fd()), C.__u8(command))
	if result == -1 {
		return 0, i2c.errMsg()
	}
	if result >= 1<<15 {
		result -= 1 << 16
	}
	return int16(result), nil
}

func (i2c *I2C) WriteInt16Cmd(command uint8, value int16) error {
	result := C.i2c_smbus_write_word_data(C.int(i2c.file.Fd()), C.__u8(command), C.__u16(value))
	if result == -1 {
		return i2c.errMsg()
	}
	return nil
}

func (i2c *I2C) WriteQuick() error {
	result := C.i2c_smbus_write_quick(C.int(i2c.file.Fd()), C.I2C_SMBUS_WRITE)
	if result != 0 {
		return i2c.errMsg()
	}
	return nil
}

func (i2c *I2C) ProcessCall(command uint8, value uint16) error {
	result := C.i2c_smbus_process_call(C.int(i2c.file.Fd()), C.__u8(command), C.__u16(value))
	if result == -1 {
		return i2c.errMsg()
	}
	return nil
}

func (i2c *I2C) Read(data []byte) (n int, err error) {
	return
}

func (i2c *I2C) Write(data []byte) (n int, err error) {
	return
}

func (i2c *I2C) Close() error {
	return i2c.file.Close()
}
<commit_msg>finished I2C<commit_after>package bbio

// #include <stddef.h>
// #include <sys/types.h>
// #include <linux/i2c-dev.h>
import "C"

import (
	"fmt"
	"os"
	"syscall"
	"unsafe"
)

// I2C is a port of https://github.com/bivab/smbus-cffi/
type I2C struct {
	file    *os.File
	address int
}

// Connects the object to the specified SMBus.
func NewI2C(bus, address int) (*I2C, error) {
	filename := fmt.Sprintf("/dev/i2c-%d", bus)
	file, err := os.OpenFile(filename, os.O_RDWR, 0)
	if err != nil {
		return nil, err
	}

	i2c := &I2C{file: file, address: -1}
	err = i2c.SetAddress(address)
	if err != nil {
		file.Close()
		return nil, err
	}

	return i2c, nil
}

func (i2c *I2C) Address() int {
	return i2c.address
}

func (i2c *I2C) SetAddress(address int) error {
	if address != i2c.address {
		result, _, err := syscall.Syscall(syscall.SYS_IOCTL, i2c.file.Fd(), C.I2C_SLAVE, uintptr(address))
		if result != 0 {
			return err
		}
		i2c.address = address
	}
	return nil
}

// Perform SMBus Read Byte transaction.
func (i2c *I2C) ReadUint8() (uint8, error) {
	result, err := C.i2c_smbus_read_byte(C.int(i2c.file.Fd()))
	if result == -1 {
		return 0, err
	}
	return uint8(result), nil
}

// Perform SMBus Write Byte transaction.
func (i2c *I2C) WriteUint8(value uint8) error {
	result, err := C.i2c_smbus_write_byte(C.int(i2c.file.Fd()), C.__u8(value))
	if result == -1 {
		return err
	}
	return nil
}

// Perform SMBus Read Byte transaction.
func (i2c *I2C) ReadInt8() (int8, error) {
	result, err := C.i2c_smbus_read_byte(C.int(i2c.file.Fd()))
	if result == -1 {
		return 0, err
	}
	if result >= 1<<7 {
		result -= 1 << 8
	}
	return int8(result), nil
}

// Perform SMBus Write Byte transaction.
func (i2c *I2C) WriteInt8(value int8) error {
	return i2c.WriteUint8(uint8(value))
}

// Perform SMBus Read Byte Data transaction.
func (i2c *I2C) ReadUint8Cmd(command uint8) (uint8, error) {
	result, err := C.i2c_smbus_read_byte_data(C.int(i2c.file.Fd()), C.__u8(command))
	if result == -1 {
		return 0, err
	}
	return uint8(result), nil
}

// Perform SMBus Write Byte Data transaction.
func (i2c *I2C) WriteUint8Cmd(command uint8, value uint8) error {
	result, err := C.i2c_smbus_write_byte_data(C.int(i2c.file.Fd()), C.__u8(command), C.__u8(value))
	if result == -1 {
		return err
	}
	return nil
}

// Perform SMBus Read Byte Data transaction.
func (i2c *I2C) ReadInt8Cmd(command uint8) (int8, error) {
	result, err := C.i2c_smbus_read_byte_data(C.int(i2c.file.Fd()), C.__u8(command))
	if result == -1 {
		return 0, err
	}
	if result >= 1<<7 {
		result -= 1 << 8
	}
	return int8(result), nil
}

// Perform SMBus Write Byte Data transaction.
func (i2c *I2C) WriteInt8Cmd(command uint8, value int8) error {
	return i2c.WriteUint8Cmd(command, uint8(value))
}

// Perform SMBus Read Word Data transaction.
func (i2c *I2C) ReadUint16Cmd(command uint8) (uint16, error) {
	result, err := C.i2c_smbus_read_word_data(C.int(i2c.file.Fd()), C.__u8(command))
	if result == -1 {
		return 0, err
	}
	return uint16(result), nil
}

// Perform SMBus Write Word Data transaction.
func (i2c *I2C) WriteUint16Cmd(command uint8, value uint16) error {
	result, err := C.i2c_smbus_write_word_data(C.int(i2c.file.Fd()), C.__u8(command), C.__u16(value))
	if result == -1 {
		return err
	}
	return nil
}

// Perform SMBus Read Word Data transaction.
func (i2c *I2C) ReadInt16Cmd(command uint8) (int16, error) {
	result, err := C.i2c_smbus_read_word_data(C.int(i2c.file.Fd()), C.__u8(command))
	if result == -1 {
		return 0, err
	}
	if result >= 1<<15 {
		result -= 1 << 16
	}
	return int16(result), nil
}

// Perform SMBus Write Word Data transaction.
func (i2c *I2C) WriteInt16Cmd(command uint8, value int16) error {
	return i2c.WriteUint16Cmd(command, uint16(value))
}

// Perform SMBus Quick transaction.
func (i2c *I2C) WriteQuick() error {
	result, err := C.i2c_smbus_write_quick(C.int(i2c.file.Fd()), C.I2C_SMBUS_WRITE)
	if result != 0 {
		return err
	}
	return nil
}

// Perform SMBus Process Call transaction.
//
// Note: although i2c_smbus_process_call returns a value, according to
// smbusmodule.c this method does not return a value by default.
//
// Set _compat = False on the SMBus instance to get a return value.
func (i2c *I2C) ProcessCall(command uint8, value uint16) (uint16, error) {
	result, err := C.i2c_smbus_process_call(C.int(i2c.file.Fd()), C.__u8(command), C.__u16(value))
	if result == -1 {
		return 0, err
	}
	return uint16(result), nil
}

// Perform SMBus Block Process Call transaction.
func (i2c *I2C) ProcessCallBlock(command uint8, block []byte) ([]byte, error) {
	length := len(block)
	if length == 0 || length > C.I2C_SMBUS_BLOCK_MAX {
		return nil, fmt.Errorf("Length of block is %d, but must be in the range 1 to %d", length, C.I2C_SMBUS_BLOCK_MAX)
	}
	data := make([]byte, length+1, C.I2C_SMBUS_BLOCK_MAX+2)
	data[0] = byte(length)
	copy(data[1:], block)
	result, err := C.i2c_smbus_access(C.int(i2c.file.Fd()), C.I2C_SMBUS_WRITE, C.__u8(command), C.I2C_SMBUS_BLOCK_PROC_CALL, (*C.union_i2c_smbus_data)(unsafe.Pointer(&data[0])))
	if result != 0 {
		return nil, err
	}
	return data[1 : 1+data[0]], nil
}

// Perform SMBus Read Block Data transaction.
func (i2c *I2C) ReadBlock(command uint8) ([]byte, error) {
	data := make([]byte, C.I2C_SMBUS_BLOCK_MAX+2)
	result, err := C.i2c_smbus_access(C.int(i2c.file.Fd()), C.I2C_SMBUS_READ, C.__u8(command), C.I2C_SMBUS_BLOCK_DATA, (*C.union_i2c_smbus_data)(unsafe.Pointer(&data[0])))
	if result != 0 {
		return nil, err
	}
	return data[1 : 1+data[0]], nil
}

// Perform SMBus Write Block Data transaction.
func (i2c *I2C) WriteBlock(command uint8, block []byte) error {
	length := len(block)
	if length == 0 || length > C.I2C_SMBUS_BLOCK_MAX {
		return fmt.Errorf("Length of block is %d, but must be in the range 1 to %d", length, C.I2C_SMBUS_BLOCK_MAX)
	}
	data := make([]byte, length+1)
	data[0] = byte(length)
	copy(data[1:], block)
	result, err := C.i2c_smbus_access(C.int(i2c.file.Fd()), C.I2C_SMBUS_WRITE, C.__u8(command), C.I2C_SMBUS_BLOCK_DATA, (*C.union_i2c_smbus_data)(unsafe.Pointer(&data[0])))
	if result != 0 {
		return err
	}
	return nil
}

// TODO: Perform I2C Block Read transaction.
// With if len == 32 then arg = C.I2C_SMBUS_I2C_BLOCK_BROKEN

func (i2c *I2C) Close() error {
	return i2c.file.Close()
}
<|endoftext|>