<commit_before>// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements.  See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with
// the License.  You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package typex

import (
	"reflect"
	"time"

	"github.com/apache/beam/sdks/v2/go/pkg/beam/core/graph/mtime"
)

// This file defines data types that programs use to indicate a
// data value is representing a particular Beam concept.

var (
	TType = reflect.TypeOf((*T)(nil)).Elem()
	UType = reflect.TypeOf((*U)(nil)).Elem()
	VType = reflect.TypeOf((*V)(nil)).Elem()
	WType = reflect.TypeOf((*W)(nil)).Elem()
	XType = reflect.TypeOf((*X)(nil)).Elem()
	YType = reflect.TypeOf((*Y)(nil)).Elem()
	ZType = reflect.TypeOf((*Z)(nil)).Elem()

	EventTimeType = reflect.TypeOf((*EventTime)(nil)).Elem()
	WindowType    = reflect.TypeOf((*Window)(nil)).Elem()
	PaneInfoType  = reflect.TypeOf((*PaneInfo)(nil)).Elem()

	KVType            = reflect.TypeOf((*KV)(nil)).Elem()
	NullableType      = reflect.TypeOf((*Nullable)(nil)).Elem()
	CoGBKType         = reflect.TypeOf((*CoGBK)(nil)).Elem()
	WindowedValueType = reflect.TypeOf((*WindowedValue)(nil)).Elem()
	BundleFinalizationType = reflect.TypeOf((*BundleFinalization)(nil)).Elem()
)

// T, U, V, W, X, Y, Z are universal types. They play the role of generic
// type variables in UserFn signatures, but are limited to top-level positions.

type T interface{}
type U interface{}
type V interface{}
type W interface{}
type X interface{}
type Y interface{}
type Z interface{}

// EventTime is a timestamp that Beam understands as attached to an element.
type EventTime = mtime.Time

// Window represents a concrete Window.
type Window interface {
	// MaxTimestamp returns the the inclusive upper bound of timestamps for values in this window.
	MaxTimestamp() EventTime

	// Equals returns true iff the windows are identical.
	Equals(o Window) bool
}

// BundleFinalization allows registering callbacks to be performed after the runner durably persists bundle results.
type BundleFinalization interface {
	RegisterCallback(time.Duration, func() error)
}

type PaneTiming byte

const (
	PaneEarly   PaneTiming = 0
	PaneOnTime  PaneTiming = 1
	PaneLate    PaneTiming = 2
	PaneUnknown PaneTiming = 3
)

type PaneInfo struct {
	Timing                     PaneTiming
	IsFirst, IsLast            bool
	Index, NonSpeculativeIndex int64
}

// KV, CoGBK, WindowedValue represent composite generic types. They are not used
// directly in user code signatures, but only in FullTypes.

type KV struct{}

type Nullable struct{}

type CoGBK struct{}

type WindowedValue struct{}
<commit_msg>Fix go fmt break in core/typex/special.go (#17266)<commit_after>// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements.  See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with
// the License.  You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package typex

import (
	"reflect"
	"time"

	"github.com/apache/beam/sdks/v2/go/pkg/beam/core/graph/mtime"
)

// This file defines data types that programs use to indicate a
// data value is representing a particular Beam concept.

var (
	TType = reflect.TypeOf((*T)(nil)).Elem()
	UType = reflect.TypeOf((*U)(nil)).Elem()
	VType = reflect.TypeOf((*V)(nil)).Elem()
	WType = reflect.TypeOf((*W)(nil)).Elem()
	XType = reflect.TypeOf((*X)(nil)).Elem()
	YType = reflect.TypeOf((*Y)(nil)).Elem()
	ZType = reflect.TypeOf((*Z)(nil)).Elem()

	EventTimeType = reflect.TypeOf((*EventTime)(nil)).Elem()
	WindowType    = reflect.TypeOf((*Window)(nil)).Elem()
	PaneInfoType  = reflect.TypeOf((*PaneInfo)(nil)).Elem()

	KVType                 = reflect.TypeOf((*KV)(nil)).Elem()
	NullableType           = reflect.TypeOf((*Nullable)(nil)).Elem()
	CoGBKType              = reflect.TypeOf((*CoGBK)(nil)).Elem()
	WindowedValueType      = reflect.TypeOf((*WindowedValue)(nil)).Elem()
	BundleFinalizationType = reflect.TypeOf((*BundleFinalization)(nil)).Elem()
)

// T, U, V, W, X, Y, Z are universal types. They play the role of generic
// type variables in UserFn signatures, but are limited to top-level positions.

type T interface{}
type U interface{}
type V interface{}
type W interface{}
type X interface{}
type Y interface{}
type Z interface{}

// EventTime is a timestamp that Beam understands as attached to an element.
type EventTime = mtime.Time

// Window represents a concrete Window.
type Window interface {
	// MaxTimestamp returns the the inclusive upper bound of timestamps for values in this window.
	MaxTimestamp() EventTime

	// Equals returns true iff the windows are identical.
	Equals(o Window) bool
}

// BundleFinalization allows registering callbacks to be performed after the runner durably persists bundle results.
type BundleFinalization interface {
	RegisterCallback(time.Duration, func() error)
}

type PaneTiming byte

const (
	PaneEarly   PaneTiming = 0
	PaneOnTime  PaneTiming = 1
	PaneLate    PaneTiming = 2
	PaneUnknown PaneTiming = 3
)

type PaneInfo struct {
	Timing                     PaneTiming
	IsFirst, IsLast            bool
	Index, NonSpeculativeIndex int64
}

// KV, CoGBK, WindowedValue represent composite generic types. They are not used
// directly in user code signatures, but only in FullTypes.

type KV struct{}

type Nullable struct{}

type CoGBK struct{}

type WindowedValue struct{}
<|endoftext|>


<commit_before>package arm

import (
	"context"
	"fmt"
	"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-03-01/compute"
	"github.com/hashicorp/packer/builder/azure/common/constants"
	"github.com/hashicorp/packer/helper/multistep"
	"github.com/hashicorp/packer/packer"
)

type StepPublishToSharedImageGallery struct {
	client  *AzureClient
	publish func(ctx context.Context, mdiID, miSigPubRg, miSIGalleryName, miSGImageName, miSGImageVersion string, miSigReplicationRegions []string, location string, tags map[string]*string) error
	say     func(message string)
	error   func(e error)
	toSIG   func() bool
}

func NewStepPublishToSharedImageGallery(client *AzureClient, ui packer.Ui, config *Config) *StepPublishToSharedImageGallery {
	var step = &StepPublishToSharedImageGallery{
		client: client,
		say: func(message string) {
			ui.Say(message)
		},
		error: func(e error) {
			ui.Error(e.Error())
		},
		toSIG: func() bool {
			return config.isManagedImage() && config.SharedGalleryDestination.SigDestinationGalleryName != ""
		},
	}

	step.publish = step.publishToSig
	return step
}

func (s *StepPublishToSharedImageGallery) publishToSig(ctx context.Context, mdiID string, miSigPubRg string, miSIGalleryName string, miSGImageName string, miSGImageVersion string, miSigReplicationRegions []string, location string, tags map[string]*string) error {

	replicationRegions := make([]compute.TargetRegion, len(miSigReplicationRegions))
	for i, v := range miSigReplicationRegions {
		regionName := v
		replicationRegions[i] = compute.TargetRegion{Name: &regionName}
	}

	galleryImageVersion := compute.GalleryImageVersion{
		Location: &location,
		Tags:     tags,
		GalleryImageVersionProperties: &compute.GalleryImageVersionProperties{
			PublishingProfile: &compute.GalleryImageVersionPublishingProfile{
				Source: &compute.GalleryArtifactSource{
					ManagedImage: &compute.ManagedArtifact{
						ID: &mdiID,
					},
				},
				TargetRegions: &replicationRegions,
			},
		},
	}

	f, err := s.client.GalleryImageVersionsClient.CreateOrUpdate(ctx, miSigPubRg, miSIGalleryName, miSGImageName, miSGImageVersion, galleryImageVersion)

	if err != nil {
		s.say(s.client.LastError.Error())
		return err
	}

	err = f.WaitForCompletionRef(ctx, s.client.GalleryImageVersionsClient.Client)

	if err != nil {
		s.say(s.client.LastError.Error())
		return err
	}

	createdSGImageVersion, err := f.Result(s.client.GalleryImageVersionsClient)

	if err != nil {
		s.say(s.client.LastError.Error())
		return err
	}

	s.say(fmt.Sprintf(" -> Shared Gallery Image Version ID : '%s'", *(createdSGImageVersion.ID)))
	return nil
}

func (s *StepPublishToSharedImageGallery) Run(ctx context.Context, stateBag multistep.StateBag) multistep.StepAction {
	if !s.toSIG() {
		return multistep.ActionContinue
	}

	s.say("Publishing to Shared Image Gallery ...")

	var miSigPubRg = stateBag.Get(constants.ArmManagedImageSigPublishResourceGroup).(string)
	var miSIGalleryName = stateBag.Get(constants.ArmManagedImageSharedGalleryName).(string)
	var miSGImageName = stateBag.Get(constants.ArmManagedImageSharedGalleryImageName).(string)
	var miSGImageVersion = stateBag.Get(constants.ArmManagedImageSharedGalleryImageVersion).(string)
	var location = stateBag.Get(constants.ArmLocation).(string)
	var tags = stateBag.Get(constants.ArmTags).(map[string]*string)
	var miSigReplicationRegions = stateBag.Get(constants.ArmManagedImageSharedGalleryReplicationRegions).([]string)
	var targetManagedImageResourceGroupName = stateBag.Get(constants.ArmLocation).(string)
	var targetManagedImageName = stateBag.Get(constants.ArmManagedImageName).(string)
	var managedImageSubscription = stateBag.Get(constants.ArmManagedImageSubscription).(string)
	var mdiID = fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Compute/images/%s", managedImageSubscription, targetManagedImageResourceGroupName, targetManagedImageName)

	s.say(fmt.Sprintf(" -> MDI ID used for SIG publish     : '%s'", mdiID))
	s.say(fmt.Sprintf(" -> SIG publish resource group     : '%s'", miSigPubRg))
	s.say(fmt.Sprintf(" -> SIG gallery name     : '%s'", miSIGalleryName))
	s.say(fmt.Sprintf(" -> SIG image name     : '%s'", miSGImageName))
	s.say(fmt.Sprintf(" -> SIG image version     : '%s'", miSGImageVersion))
	s.say(fmt.Sprintf(" -> SIG replication regions    : '%v'", miSigReplicationRegions))
	s.say(fmt.Sprintf(" -> SIG publish location     : '%s'", location))
	s.say(fmt.Sprintf(" -> SIG publish tags     : '%v'", tags))
	err := s.publish(ctx, mdiID, miSigPubRg, miSIGalleryName, miSGImageName, miSGImageVersion, miSigReplicationRegions, location, tags)

	if err != nil {
		stateBag.Put(constants.Error, err)
		s.error(err)

		return multistep.ActionHalt
	}

	return multistep.ActionContinue
}

func (*StepPublishToSharedImageGallery) Cleanup(multistep.StateBag) {
}
<commit_msg>fix bug in step_publish w.r.t managed image RG<commit_after>package arm

import (
	"context"
	"fmt"
	"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-03-01/compute"
	"github.com/hashicorp/packer/builder/azure/common/constants"
	"github.com/hashicorp/packer/helper/multistep"
	"github.com/hashicorp/packer/packer"
)

type StepPublishToSharedImageGallery struct {
	client  *AzureClient
	publish func(ctx context.Context, mdiID, miSigPubRg, miSIGalleryName, miSGImageName, miSGImageVersion string, miSigReplicationRegions []string, location string, tags map[string]*string) error
	say     func(message string)
	error   func(e error)
	toSIG   func() bool
}

func NewStepPublishToSharedImageGallery(client *AzureClient, ui packer.Ui, config *Config) *StepPublishToSharedImageGallery {
	var step = &StepPublishToSharedImageGallery{
		client: client,
		say: func(message string) {
			ui.Say(message)
		},
		error: func(e error) {
			ui.Error(e.Error())
		},
		toSIG: func() bool {
			return config.isManagedImage() && config.SharedGalleryDestination.SigDestinationGalleryName != ""
		},
	}

	step.publish = step.publishToSig
	return step
}

func (s *StepPublishToSharedImageGallery) publishToSig(ctx context.Context, mdiID string, miSigPubRg string, miSIGalleryName string, miSGImageName string, miSGImageVersion string, miSigReplicationRegions []string, location string, tags map[string]*string) error {

	replicationRegions := make([]compute.TargetRegion, len(miSigReplicationRegions))
	for i, v := range miSigReplicationRegions {
		regionName := v
		replicationRegions[i] = compute.TargetRegion{Name: &regionName}
	}

	galleryImageVersion := compute.GalleryImageVersion{
		Location: &location,
		Tags:     tags,
		GalleryImageVersionProperties: &compute.GalleryImageVersionProperties{
			PublishingProfile: &compute.GalleryImageVersionPublishingProfile{
				Source: &compute.GalleryArtifactSource{
					ManagedImage: &compute.ManagedArtifact{
						ID: &mdiID,
					},
				},
				TargetRegions: &replicationRegions,
			},
		},
	}

	f, err := s.client.GalleryImageVersionsClient.CreateOrUpdate(ctx, miSigPubRg, miSIGalleryName, miSGImageName, miSGImageVersion, galleryImageVersion)

	if err != nil {
		s.say(s.client.LastError.Error())
		return err
	}

	err = f.WaitForCompletionRef(ctx, s.client.GalleryImageVersionsClient.Client)

	if err != nil {
		s.say(s.client.LastError.Error())
		return err
	}

	createdSGImageVersion, err := f.Result(s.client.GalleryImageVersionsClient)

	if err != nil {
		s.say(s.client.LastError.Error())
		return err
	}

	s.say(fmt.Sprintf(" -> Shared Gallery Image Version ID : '%s'", *(createdSGImageVersion.ID)))
	return nil
}

func (s *StepPublishToSharedImageGallery) Run(ctx context.Context, stateBag multistep.StateBag) multistep.StepAction {
	if !s.toSIG() {
		return multistep.ActionContinue
	}

	s.say("Publishing to Shared Image Gallery ...")

	var miSigPubRg = stateBag.Get(constants.ArmManagedImageSigPublishResourceGroup).(string)
	var miSIGalleryName = stateBag.Get(constants.ArmManagedImageSharedGalleryName).(string)
	var miSGImageName = stateBag.Get(constants.ArmManagedImageSharedGalleryImageName).(string)
	var miSGImageVersion = stateBag.Get(constants.ArmManagedImageSharedGalleryImageVersion).(string)
	var location = stateBag.Get(constants.ArmLocation).(string)
	var tags = stateBag.Get(constants.ArmTags).(map[string]*string)
	var miSigReplicationRegions = stateBag.Get(constants.ArmManagedImageSharedGalleryReplicationRegions).([]string)
	var targetManagedImageResourceGroupName = stateBag.Get(constants.ArmManagedImageResourceGroupName).(string)
	var targetManagedImageName = stateBag.Get(constants.ArmManagedImageName).(string)
	var managedImageSubscription = stateBag.Get(constants.ArmManagedImageSubscription).(string)
	var mdiID = fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Compute/images/%s", managedImageSubscription, targetManagedImageResourceGroupName, targetManagedImageName)

	s.say(fmt.Sprintf(" -> MDI ID used for SIG publish     : '%s'", mdiID))
	s.say(fmt.Sprintf(" -> SIG publish resource group     : '%s'", miSigPubRg))
	s.say(fmt.Sprintf(" -> SIG gallery name     : '%s'", miSIGalleryName))
	s.say(fmt.Sprintf(" -> SIG image name     : '%s'", miSGImageName))
	s.say(fmt.Sprintf(" -> SIG image version     : '%s'", miSGImageVersion))
	s.say(fmt.Sprintf(" -> SIG replication regions    : '%v'", miSigReplicationRegions))
	s.say(fmt.Sprintf(" -> SIG publish location     : '%s'", location))
	s.say(fmt.Sprintf(" -> SIG publish tags     : '%v'", tags))
	err := s.publish(ctx, mdiID, miSigPubRg, miSIGalleryName, miSGImageName, miSGImageVersion, miSigReplicationRegions, location, tags)

	if err != nil {
		stateBag.Put(constants.Error, err)
		s.error(err)

		return multistep.ActionHalt
	}

	return multistep.ActionContinue
}

func (*StepPublishToSharedImageGallery) Cleanup(multistep.StateBag) {
}
<|endoftext|>


<commit_before>package nodes

import (
	"sort"
	"sync"
	"time"

	"github.com/hashicorp/nomad/api"
	"github.com/jippi/hashi-ui/backend/nomad/helper"
	"github.com/jippi/hashi-ui/backend/structs"
)

const (
	WatchList   = "NOMAD_WATCH_NODES"
	UnwatchList = "NOMAD_UNWATCH_NODES"
	fetchedList = "NOMAD_FETCHED_NODES"
)

type customClient struct {
	*api.NodeListStub
	Stats map[string]interface{}
}

type list struct {
	action structs.Action
	client *api.Client
	query  *api.QueryOptions
	last   []*api.NodeListStub
}

func NewList(action structs.Action, client *api.Client, query *api.QueryOptions) *list {
	if query != nil {
		query.WaitTime = 10 * time.Second
	}

	return &list{
		action: action,
		client: client,
		query:  query,
	}
}

func (w *list) Do() (*structs.Response, error) {
	nodes, meta, err := w.client.Nodes().List(w.query)
	if err != nil {
		return structs.NewErrorResponse(err)
	}

	if !helper.QueryChanged(w.query, meta) {
		structs.NewResponseWithIndex(fetchedList, nodeStats(w.client, w.last), meta.LastIndex)
	}

	// http://stackoverflow.com/a/28999886
	// TODO: refactor to Go 1.9 sorting !
	sort.Sort(ClientNameSorter(nodes))

	w.last = nodes

	return structs.NewResponseWithIndex(fetchedList, nodeStats(w.client, nodes), meta.LastIndex)
}

func (w *list) Key() string {
	return "/nodes/list"
}

func (w *list) IsMutable() bool {
	return false
}

func nodeStats(client *api.Client, nodes []*api.NodeListStub) []*customClient {
	var wg sync.WaitGroup
	res := make([]*customClient, len(nodes))

	for i, node := range nodes {
		wg.Add(1)
		go func(i int, node *api.NodeListStub) {
			defer wg.Done()

			if node.Status != "ready" {
				res[i] = &customClient{node, make(map[string]interface{})}
				return
			}

			stats, err := client.Nodes().Stats(node.ID, nil)
			if err != nil {
				res[i] = &customClient{node, make(map[string]interface{})}
				return
			}

			comp := make(map[string]interface{})
			comp["cpu"] = cpu(stats.CPU)

			res[i] = &customClient{node, comp}
		}(i, node)
	}

	wg.Wait()

	return res
}

func cpu(cpus []*api.HostCPUStats) int {
	var sum float64

	for _, cpu := range cpus {
		sum = sum + (100 - cpu.Idle)
	}

	return int(sum / float64(len(cpus)))
}

type ClientNameSorter []*api.NodeListStub

func (a ClientNameSorter) Len() int           { return len(a) }
func (a ClientNameSorter) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a ClientNameSorter) Less(i, j int) bool { return a[i].Name < a[j].Name }
<commit_msg>more clean fix to clients being offline<commit_after>package nodes

import (
	"sort"
	"sync"
	"time"

	"github.com/hashicorp/nomad/api"
	"github.com/jippi/hashi-ui/backend/nomad/helper"
	"github.com/jippi/hashi-ui/backend/structs"
)

const (
	WatchList   = "NOMAD_WATCH_NODES"
	UnwatchList = "NOMAD_UNWATCH_NODES"
	fetchedList = "NOMAD_FETCHED_NODES"
)

type customClient struct {
	*api.NodeListStub
	Stats map[string]interface{}
}

type list struct {
	action structs.Action
	client *api.Client
	query  *api.QueryOptions
	last   []*api.NodeListStub
}

func NewList(action structs.Action, client *api.Client, query *api.QueryOptions) *list {
	if query != nil {
		query.WaitTime = 10 * time.Second
	}

	return &list{
		action: action,
		client: client,
		query:  query,
	}
}

func (w *list) Do() (*structs.Response, error) {
	nodes, meta, err := w.client.Nodes().List(w.query)
	if err != nil {
		return structs.NewErrorResponse(err)
	}

	if !helper.QueryChanged(w.query, meta) {
		structs.NewResponseWithIndex(fetchedList, nodeStats(w.client, w.last), meta.LastIndex)
	}

	// http://stackoverflow.com/a/28999886
	// TODO: refactor to Go 1.9 sorting !
	sort.Sort(ClientNameSorter(nodes))

	w.last = nodes

	return structs.NewResponseWithIndex(fetchedList, nodeStats(w.client, nodes), meta.LastIndex)
}

func (w *list) Key() string {
	return "/nodes/list"
}

func (w *list) IsMutable() bool {
	return false
}

func nodeStats(client *api.Client, nodes []*api.NodeListStub) []*customClient {
	var wg sync.WaitGroup
	res := make([]*customClient, len(nodes))

	for i, node := range nodes {
		wg.Add(1)
		go func(i int, node *api.NodeListStub) {
			defer wg.Done()

			res[i] = &customClient{node, make(map[string]interface{})}

			if node.Status != "ready" {
				return
			}

			stats, err := client.Nodes().Stats(node.ID, nil)
			if err != nil {
				return
			}

			comp := make(map[string]interface{})
			comp["cpu"] = cpu(stats.CPU)

			res[i].Stats = comp
		}(i, node)
	}

	wg.Wait()

	return res
}

func cpu(cpus []*api.HostCPUStats) int {
	var sum float64

	for _, cpu := range cpus {
		sum = sum + (100 - cpu.Idle)
	}

	return int(sum / float64(len(cpus)))
}

type ClientNameSorter []*api.NodeListStub

func (a ClientNameSorter) Len() int           { return len(a) }
func (a ClientNameSorter) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a ClientNameSorter) Less(i, j int) bool { return a[i].Name < a[j].Name }
<|endoftext|>


<commit_before>package main

func main() {}
<commit_msg>nilinterface: add ex1<commit_after>package main

func main() {
	ex1()

}

func ex1() {
	var i interface{} = nil
	println("nil interface is nil?:", i == nil) // true
	println(i)                                  // (0x0, 0x0)

}
<|endoftext|>


<commit_before>package bbio

// #include <linux/spi/spidev.h>
// #include <sys/ioctl.h>
import "C"

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

const (
	SPI_CPHA       = 0x01  /* clock phase */
	SPI_CPOL       = 0x02  /* clock polarity */
	SPI_CS_HIGH    = 0x04  /* chipselect active high? */
	SPI_LSB_FIRST  = 0x08  /* per-word bits-on-wire */
	SPI_TRHEE_WIRE = 0x10  /* SI/SO signals shared */
	SPI_LOOP       = 0x20  /* loopback mode */
	SPI_NO_CS      = 0x40  /* 1 dev/bus, no chipselect */
	SPI_READY      = 0x80  /* slave pulls low to pause */
	SPI_TX_DUAL    = 0x100 /* transmit with 2 wires */
	SPI_TX_QUAD    = 0x200 /* transmit with 4 wires */
	SPI_RX_DUAL    = 0x400 /* receive with 2 wires */
	SPI_RX_QUAD    = 0x800 /* receive with 4 wires */
)

type SPIMode uint8

const (
	SPI_MODE_0 SPIMode = 0 /* (original MicroWire) */
	SPI_MODE_1 SPIMode = SPI_CPHA
	SPI_MODE_2 SPIMode = SPI_CPOL
	SPI_MODE_3 SPIMode = SPI_CPOL | SPI_CPHA
)

type SPI struct {
	file *os.File /* open file descriptor: /dev/spi-X.Y */
	mode uint8    /* current SPI mode */
	bpw  uint8    /* current SPI bits per word setting */
	msh  uint32   /* current SPI max speed setting in Hz */
}

func NewSPI() (*SPI, error) {
	return new(SPI), nil
}

func (spi *SPI) Read(data []byte) (n int, err error) {
	return spi.file.Read(data)
}

func (spi *SPI) Write(data []byte) (n int, err error) {
	return spi.file.Write(data)
}

func (spi *SPI) Close() error {
	return spi.file.Close()
}

func (spi *SPI) Mode() SPIMode {
	return SPIMode(spi.mode) & SPI_MODE_3
}

func (spi *SPI) SetMode(mode SPIMode) error {
	m := (spi.mode &^ (SPI_CPHA | SPI_CPOL)) | uint8(mode)
	err := spi.setMode(m)
	if err == nil {
		spi.mode = m
	}
	return err
}

func (spi *SPI) setMode(mode uint8) error {
	r, _, err := syscall.Syscall(syscall.SYS_IOCTL, spi.file.Fd(), C.SPI_IOC_WR_MODE, uintptr(unsafe.Pointer(&mode)))
	if r != 0 {
		return err
	}

	var test uint8
	r, _, err = syscall.Syscall(syscall.SYS_IOCTL, spi.file.Fd(), C.SPI_IOC_RD_MODE, uintptr(unsafe.Pointer(&test)))
	if r != 0 {
		return err
	}

	if test == mode {
		return nil
	} else {
		return fmt.Errorf("Could not set SPI mode %d", mode)
	}
}
<commit_msg>added SPI support<commit_after>package bbio

// #include <linux/spi/spidev.h>
// #include <sys/ioctl.h>
import "C"

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

const (
	SPI_CPHA      uint8 = 0x01 /* clock phase */
	SPI_CPOL      uint8 = 0x02 /* clock polarity */
	SPI_CS_HIGH   uint8 = 0x04 /* chipselect active high? */
	SPI_LSB_FIRST uint8 = 0x08 /* per-word bits-on-wire */
	SPI_3WIRE     uint8 = 0x10 /* SI/SO signals shared */
	SPI_LOOP      uint8 = 0x20 /* loopback mode */
)

// not used yet
const (
	SPI_NO_CS   = 0x40  /* 1 dev/bus, no chipselect */
	SPI_READY   = 0x80  /* slave pulls low to pause */
	SPI_TX_DUAL = 0x100 /* transmit with 2 wires */
	SPI_TX_QUAD = 0x200 /* transmit with 4 wires */
	SPI_RX_DUAL = 0x400 /* receive with 2 wires */
	SPI_RX_QUAD = 0x800 /* receive with 4 wires */
)

// SPI mode as two bit pattern of
// Clock Polarity and Phase [CPOL|CPHA]
// min: 0b00 = 0 max: 0b11 = 3
type SPIMode uint8

const (
	SPI_MODE_0 SPIMode = 0 /* (original MicroWire) */
	SPI_MODE_1 SPIMode = SPIMode(SPI_CPHA)
	SPI_MODE_2 SPIMode = SPIMode(SPI_CPOL)
	SPI_MODE_3 SPIMode = SPIMode(SPI_CPOL | SPI_CPHA)
)

type SPI struct {
	file        *os.File /* open file descriptor: /dev/spi-X.Y */
	mode        uint8    /* current SPI mode */
	bitsPerWord uint8    /* current SPI bits per word setting */
	maxSpeedHz  uint32   /* current SPI max speed setting in Hz */
}

// NewSPI returns a new SPI object that is connected to the
// specified SPI device interface.
//
// NewSPI(X,Y) will open /dev/spidev-X.Y
//
// SPI is an object type that allows SPI transactions
// on hosts running the Linux kernel. The host kernel must have SPI
// support and SPI device interface support.
// All of these can be either built-in to the kernel, or loaded from modules.
//
// Because the SPI device interface is opened R/W, users of this
// module usually must have root permissions.
func NewSPI(bus, device int) (*SPI, error) {
	deviceTreeName := fmt.Sprintf("ADAFRUIT-SPI%d", bus)
	err := LoadDeviceTree(deviceTreeName)
	if err != nil {
		return nil, err
	}

	spi := new(SPI)

	path := fmt.Sprintf("/dev/spidev%d.%d", bus+1, device)
	spi.file, err = os.OpenFile(path, os.O_RDWR, 0)
	if err != nil {
		return nil, err
	}

	r, _, err := syscall.Syscall(syscall.SYS_IOCTL, spi.file.Fd(), C.SPI_IOC_RD_MODE, uintptr(unsafe.Pointer(&spi.mode)))
	if r != 0 {
		return nil, err
	}

	r, _, err = syscall.Syscall(syscall.SYS_IOCTL, spi.file.Fd(), C.SPI_IOC_RD_BITS_PER_WORD, uintptr(unsafe.Pointer(&spi.bitsPerWord)))
	if r != 0 {
		return nil, err
	}

	r, _, err = syscall.Syscall(syscall.SYS_IOCTL, spi.file.Fd(), C.SPI_IOC_RD_MAX_SPEED_HZ, uintptr(unsafe.Pointer(&spi.maxSpeedHz)))
	if r != 0 {
		return nil, err
	}

	return spi, nil
}

// Read len(data) bytes from SPI device.
func (spi *SPI) Read(data []byte) (n int, err error) {
	return spi.file.Read(data)
}

// Write data to SPI device.
func (spi *SPI) Write(data []byte) (n int, err error) {
	return spi.file.Write(data)
}

type spi_ioc_transfer struct {
	tx_buf        uintptr
	rx_buf        uintptr
	len           uint32
	speed_hz      uint32
	delay_usecs   uint16
	bits_per_word uint8
	cs_change     uint8
	pad           uint32
}

// Xfer performs a SPI transaction.
// CS will be released and reactivated between blocks.
// delay specifies delay in usec between blocks.
func (spi *SPI) Xfer(txBuf []byte, delay_usecs uint16) (rxBuf []byte, err error) {
	length := len(txBuf)
	rxBuf = make([]byte, length)

	xfer := make([]spi_ioc_transfer, length)
	for i := range xfer {
		xfer[i].tx_buf = uintptr(unsafe.Pointer(&txBuf[i]))
		xfer[i].rx_buf = uintptr(unsafe.Pointer(&rxBuf[i]))
		xfer[i].len = 1
		xfer[i].delay_usecs = delay_usecs
	}

	SPI_IOC_MESSAGE := C._IOC_WRITE<<C._IOC_DIRSHIFT | C.SPI_IOC_MAGIC<<C._IOC_TYPESHIFT | length<<C._IOC_SIZESHIFT

	r, _, err := syscall.Syscall(syscall.SYS_IOCTL, spi.file.Fd(), uintptr(SPI_IOC_MESSAGE), uintptr(unsafe.Pointer(&xfer[0])))
	if r != 0 {
		return nil, err
	}

	// WA:
	// in CS_HIGH mode CS isn't pulled to low after transfer, but after read
	// reading 0 bytes doesnt matter but brings cs down
	syscall.Syscall(syscall.SYS_READ, spi.file.Fd(), uintptr(unsafe.Pointer(&rxBuf[0])), 0)

	return rxBuf, nil
}

// Xfer2 performs a SPI transaction.
// CS will be held active between blocks.
func (spi *SPI) Xfer2(txBuf []byte, delay_usecs uint16) (rxBuf []byte, err error) {
	length := len(txBuf)
	rxBuf = make([]byte, length)

	xfer := spi_ioc_transfer{
		tx_buf: uintptr(unsafe.Pointer(&txBuf[0])),
		rx_buf: uintptr(unsafe.Pointer(&rxBuf[0])),
		len:    uint32(length),
	}

	SPI_IOC_MESSAGE := C._IOC_WRITE<<C._IOC_DIRSHIFT | C.SPI_IOC_MAGIC<<C._IOC_TYPESHIFT | 1<<C._IOC_SIZESHIFT

	r, _, err := syscall.Syscall(syscall.SYS_IOCTL, spi.file.Fd(), uintptr(SPI_IOC_MESSAGE), uintptr(unsafe.Pointer(&xfer)))
	if r != 0 {
		return nil, err
	}

	// WA:
	// in CS_HIGH mode CS isn't pulled to low after transfer, but after read
	// reading 0 bytes doesnt matter but brings cs down
	syscall.Syscall(syscall.SYS_READ, spi.file.Fd(), uintptr(unsafe.Pointer(&rxBuf[0])), 0)

	return rxBuf, nil
}

// Disconnects the object from the interface.
func (spi *SPI) Close() error {
	return spi.file.Close()
}

func (spi *SPI) Mode() SPIMode {
	return SPIMode(spi.mode) & SPI_MODE_3
}

func (spi *SPI) SetMode(mode SPIMode) error {
	newMode := (spi.mode &^ uint8(SPI_MODE_3)) | uint8(mode)
	err := spi.setModeInt(newMode)
	if err == nil {
		spi.mode = newMode
	}
	return err
}

// CS active high
func (spi *SPI) CSHigh() bool {
	return spi.mode&SPI_CS_HIGH != 0
}

// CS active high
func (spi *SPI) SetCSHigh(csHigh bool) error {
	return spi.setModeFlag(csHigh, SPI_CS_HIGH)
}

func (spi *SPI) LSBFirst() bool {
	return spi.mode&SPI_LSB_FIRST != 0
}

func (spi *SPI) SetLSBFirst(lsbFirst bool) error {
	return spi.setModeFlag(lsbFirst, SPI_LSB_FIRST)
}

func (spi *SPI) ThreeWire() bool {
	return spi.mode&SPI_3WIRE != 0
}

func (spi *SPI) SetThreeWire(threeWire bool) error {
	return spi.setModeFlag(threeWire, SPI_3WIRE)
}

// Loop returns the loopback configuration.
func (spi *SPI) Loop() bool {
	return spi.mode&SPI_3WIRE != 0
}

// SetLoop sets the loopback configuration.
func (spi *SPI) SetLoop(loop bool) error {
	return spi.setModeFlag(loop, SPI_LOOP)
}

func (spi *SPI) BitsPerWord() uint8 {
	return spi.bitsPerWord
}

func (spi *SPI) SetBitsPerWord(bits uint8) error {
	if bits < 8 || bits > 16 {
		return fmt.Errorf("SPI bits per word %d outside of valid range 8 to 16", bits)
	}

	r, _, err := syscall.Syscall(syscall.SYS_IOCTL, spi.file.Fd(), C.SPI_IOC_WR_BITS_PER_WORD, uintptr(unsafe.Pointer(&bits)))
	if r != 0 {
		return err
	}

	var test uint8
	r, _, err = syscall.Syscall(syscall.SYS_IOCTL, spi.file.Fd(), C.SPI_IOC_RD_BITS_PER_WORD, uintptr(unsafe.Pointer(&test)))
	if r != 0 {
		return err
	}

	if test == bits {
		spi.bitsPerWord = bits
		return nil
	} else {
		return fmt.Errorf("Could not set SPI bits per word %d", bits)
	}
}

func (spi *SPI) MaxSpeedHz() uint32 {
	return spi.maxSpeedHz
}

func (spi *SPI) SetMaxSpeedHz(maxSpeedHz uint32) error {
	r, _, err := syscall.Syscall(syscall.SYS_IOCTL, spi.file.Fd(), C.SPI_IOC_WR_MAX_SPEED_HZ, uintptr(unsafe.Pointer(&maxSpeedHz)))
	if r != 0 {
		return err
	}

	var test uint32
	r, _, err = syscall.Syscall(syscall.SYS_IOCTL, spi.file.Fd(), C.SPI_IOC_RD_MAX_SPEED_HZ, uintptr(unsafe.Pointer(&test)))
	if r != 0 {
		return err
	}

	if test == maxSpeedHz {
		spi.maxSpeedHz = maxSpeedHz
		return nil
	} else {
		return fmt.Errorf("Could not set SPI max speed in hz %d", maxSpeedHz)
	}
}

func (spi *SPI) setModeFlag(flag bool, mask uint8) error {
	newMode := spi.mode
	if flag {
		newMode |= mask
	} else {
		newMode &= ^mask
	}
	err := spi.setModeInt(newMode)
	if err == nil {
		spi.mode = newMode
	}
	return err
}

func (spi *SPI) setModeInt(mode uint8) error {
	r, _, err := syscall.Syscall(syscall.SYS_IOCTL, spi.file.Fd(), C.SPI_IOC_WR_MODE, uintptr(unsafe.Pointer(&mode)))
	if r != 0 {
		return err
	}

	var test uint8
	r, _, err = syscall.Syscall(syscall.SYS_IOCTL, spi.file.Fd(), C.SPI_IOC_RD_MODE, uintptr(unsafe.Pointer(&test)))
	if r != 0 {
		return err
	}

	if test == mode {
		return nil
	} else {
		return fmt.Errorf("Could not set SPI mode %X", mode)
	}
}
<|endoftext|>


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

package main

import (
	"errors"
	"net"
	"net/http"
	"os"
	"sync"
	"time"

	"golang.org/x/net/websocket"
)

const (
	ldl = 100 * time.Millisecond
)

type SrvListener struct {
	ln    Listener
	stop  chan struct{}
	done  chan error
	ws    bool
	clean string
}

type Server struct {
	cmd       *Command
	repos     *Repos
	listeners []*SrvListener
	sessions  []*Ses
	mutex     *sync.Mutex

	listening struct {
		stop chan struct{}
		done chan struct{}
	}
}

func (cmd *Command) Server(args ...string) {
	srv := &Server{
		cmd:       cmd,
		listeners: make([]*SrvListener, 0),
		sessions:  make([]*Ses, 0),
		mutex:     &sync.Mutex{},
	}
	err := cmd.Cfg.Check(ServerMode)
	if err != nil {
		goto egress
	}
	if srv.repos, err = NewRepos(srv.cmd.Cfg.Dir); err != nil {
		goto egress
	}
	defer srv.repos.Free()
	for _, k := range []*UserKeys{
		srv.cmd.Cfg.Keys.Admin,
		srv.cmd.Cfg.Keys.Server,
	} {
		user := srv.repos.Users.Search(k.Pub.Encr)
		if user == nil {
			user, err = srv.repos.NewUser(k.Pub.Encr)
			if err != nil {
				goto egress
			}
			user.ASN.Auth = *k.Pub.Auth
			user.ASN.Author = *k.Pub.Encr
		}
		user = nil
	}
	if len(args) > 0 {
		// local server command line exec
		ses := NewSes()
		ses.srv = srv
		ses.ASN.Repos = srv.repos
		ses.Keys.Client.Login = *srv.cmd.Cfg.Keys.Admin.Pub.Encr
		ses.asnsrv = true
		v := ses.Exec(Requester{}, cmd.In, args...)
		err, _ = v.(error)
		AckOut(cmd.Out, v)
		v = nil
	} else {
		if err = srv.Listen(); err == nil {
			cmd.In.Close()
			cmd.Out.Close()
			// FIXME should we close or os.Stderr?
			Log.Println("started", cmd.Cfg.Name,
				"with", len(srv.listeners), "listener(s)")
			for {
				sig := <-srv.cmd.Sig
				Diag.Println("caught", sig)
				if IsINT(sig) || IsTERM(sig) {
					TraceFlush(Diag)
					srv.Close()
					if IsINT(sig) {
						srv.Hangup()
						break
					}
				} else if IsUSR1(sig) {
					TraceFlush(Log)
				}
			}
			Log.Println("stopped", cmd.Cfg.Name)
		}
	}
egress:
	if err != nil {
		Log.Println("ERROR:", cmd.Cfg.Name, err)
		Diag.Println("oops", err)
	}
	cmd.Done <- err
}

func (srv *Server) AddListener(l *SrvListener) {
	srv.mutex.Lock()
	defer srv.mutex.Unlock()
	for _, p := range srv.listeners {
		if p == nil {
			p = l
			return
		}
	}
	srv.listeners = append(srv.listeners, l)
}

func (srv *Server) Close() {
	for i, le := range srv.listeners {
		if le.ws {
			le.ln.Close()
		} else {
			le.stop <- struct{}{}
			<-le.done
		}
		close(le.stop)
		close(le.done)
		le.ln = nil
		srv.listeners[i] = nil
	}
	srv.listeners = nil
}

func (srv *Server) ForEachSession(f func(*Ses)) {
	srv.mutex.Lock()
	defer srv.mutex.Unlock()
	for _, ses := range srv.sessions {
		f(ses)
	}
}

func (srv *Server) Free(ses *Ses) {
	ses.ExecMutex.Lock()
	ses.ExecMutex.Unlock()
	srv.mutex.Lock()
	defer srv.mutex.Unlock()
	ses.Free()
	for i := range srv.sessions {
		if srv.sessions[i] == ses {
			copy(srv.sessions[i:], srv.sessions[i+1:])
			srv.sessions[len(srv.sessions)-1] = nil
			srv.sessions = srv.sessions[:len(srv.sessions)-1]
			break
		}
	}
}

func (srv *Server) handler(conn net.Conn) {
	ses := srv.newSes()
	defer func() {
		ses.ASN.SetStateClosed()
		srv.Free(ses)
	}()
	ses.ASN.SetConn(conn)
	conn.Read(ses.Keys.Client.Ephemeral[:])
	ses.ASN.Println("connected",
		ses.Keys.Client.Ephemeral.String()[:8]+"...")
	ses.ASN.SetBox(NewBox(2, srv.cmd.Cfg.Keys.Nonce,
		&ses.Keys.Client.Ephemeral,
		srv.cmd.Cfg.Keys.Server.Pub.Encr,
		srv.cmd.Cfg.Keys.Server.Sec.Encr))
	for {
		pdu := <-ses.ASN.RxQ
		if pdu == nil {
			break
		}
		err := pdu.Open()
		if err != nil {
			pdu.Free()
			break
		}
		var (
			v  Version
			id Id
		)
		v.ReadFrom(pdu)
		if v > ses.ASN.Version() {
			ses.ASN.SetVersion(v)
		}
		id.ReadFrom(pdu)
		id.Internal(v)
		ses.ASN.Time.Out = time.Now()
		switch id {
		case AckReqId:
			err = ses.ASN.AckerRx(pdu)
		case ExecReqId:
			err = ses.RxExec(pdu)
		case LoginReqId:
			err = ses.RxLogin(pdu)
		case PauseReqId:
			err = ses.RxPause(pdu)
		case ResumeReqId:
			err = ses.RxResume(pdu)
		case QuitReqId:
			err = ses.RxQuit(pdu)
		case BlobId:
			err = ses.RxBlob(pdu)
		default:
			if id >= Nids {
				err = ErrIncompatible
			} else {
				err = ErrUnsupported
			}
		}
		pdu.Free()
		pdu = nil
		if err != nil {
			ses.ASN.Println("Error:", err)
			break
		}
	}
}

func (srv *Server) Hangup() {
	for len(srv.sessions) > 0 {
		srv.mutex.Lock()
		ses := srv.sessions[0]
		srv.mutex.Unlock()
		ses.ASN.SetStateClosed()
		for {
			time.Sleep(100 * time.Millisecond)
			if len(srv.sessions) == 0 || srv.sessions[0] != ses {
				break
			}
		}
	}
	srv.sessions = nil
}

func (srv *Server) Listen() error {
	for _, lurl := range srv.cmd.Cfg.Listen {
		l := &SrvListener{
			stop: make(chan struct{}, 1),
			done: make(chan error, 1),
		}
		switch lurl.Scheme {
		case "tcp":
			addr, err := net.ResolveTCPAddr(lurl.Scheme, lurl.Host)
			if err != nil {
				return err
			}
			l.ln, err = net.ListenTCP(lurl.Scheme, addr)
			if err != nil {
				return err
			}
			srv.AddListener(l)
			Diag.Println(srv.cmd.Cfg.Name, "listening on", addr)
			go l.listen(srv)
		case "unix":
			path := UrlPathSearch(lurl.Path)
			os.Remove(path)
			addr, err := net.ResolveUnixAddr(lurl.Scheme, path)
			if err != nil {
				return err
			}
			l.ln, err = net.ListenUnix(lurl.Scheme, addr)
			if err != nil {
				return err
			}
			srv.AddListener(l)
			l.clean = path
			Diag.Println(srv.cmd.Cfg.Name, "listening on", addr)
			go l.listen(srv)
		case "ws":
			l.ws = true
			addr, err := net.ResolveTCPAddr("tcp", lurl.Host)
			if err != nil {
				return err
			}
			if l.ln, err = net.ListenTCP("tcp", addr); err != nil {
				return err
			}
			srv.AddListener(l)
			f := func(ws *websocket.Conn) {
				srv.handler(ws)
			}
			/*
				FIXME should use a custom handler
				h := func (w http.ResponseWriter, req *http.Request) {
					s := websocket.Server{Handler: websocket.Handler(webHandler)}
					s.ServeHTTP(w, req)
				});
				s := &http.Server{
					Addr:           ":8080",
					Handler:        h,
					ReadTimeout:    10 * time.Second,
					WriteTimeout:   10 * time.Second,
					MaxHeaderBytes: 1 << 20,
				}
				return s.Serve(l)
			*/
			/*
				http.Handle(lurl.Path, websocket.Handler(f))
				go http.Serve(l.ln, nil)
			*/
			mux := http.NewServeMux()
			mux.Handle(lurl.Path, websocket.Handler(f))
			Diag.Println(srv.cmd.Cfg.Name, "listening on", addr)
			go http.Serve(l.ln, mux)
		default:
			Log.Println("lurl:", lurl.String())
			return errors.New("unsupported scheme: " + lurl.Scheme)
		}
	}
	return nil
}

func (srv *Server) newSes() (ses *Ses) {
	srv.mutex.Lock()
	ses = NewSes()
	srv.sessions = append(srv.sessions, ses)
	ses.srv = srv
	ses.ASN.Repos = srv.repos
	ses.ASN.Name.Local = srv.cmd.Cfg.Name
	ses.ASN.Name.Remote = "unnamed"
	ses.ASN.Name.Session = ses.ASN.Name.Local + ":" + ses.ASN.Name.Remote
	srv.mutex.Unlock()
	return
}

func (l *SrvListener) listen(srv *Server) {
	for {
		select {
		case <-l.stop:
			err := l.ln.Close()
			if len(l.clean) > 0 {
				os.Remove(l.clean)
			}
			l.done <- err
			return
		default:
			l.ln.SetDeadline(time.Now().Add(ldl))
			conn, err := l.ln.Accept()
			if err == nil {
				go srv.handler(conn)
			} else if opErr, ok := err.(*net.OpError); !ok ||
				!opErr.Timeout() {
				Diag.Println("accept", err)
			}
		}
	}
}
<commit_msg>Let (*ASN).Free() SetStateClosed<commit_after>// Copyright 2014-2015 Apptimist, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"errors"
	"net"
	"net/http"
	"os"
	"sync"
	"time"

	"golang.org/x/net/websocket"
)

const (
	ldl = 100 * time.Millisecond
)

type SrvListener struct {
	ln    Listener
	stop  chan struct{}
	done  chan error
	ws    bool
	clean string
}

type Server struct {
	cmd       *Command
	repos     *Repos
	listeners []*SrvListener
	sessions  []*Ses
	mutex     *sync.Mutex

	listening struct {
		stop chan struct{}
		done chan struct{}
	}
}

func (cmd *Command) Server(args ...string) {
	srv := &Server{
		cmd:       cmd,
		listeners: make([]*SrvListener, 0),
		sessions:  make([]*Ses, 0),
		mutex:     &sync.Mutex{},
	}
	err := cmd.Cfg.Check(ServerMode)
	if err != nil {
		goto egress
	}
	if srv.repos, err = NewRepos(srv.cmd.Cfg.Dir); err != nil {
		goto egress
	}
	defer srv.repos.Free()
	for _, k := range []*UserKeys{
		srv.cmd.Cfg.Keys.Admin,
		srv.cmd.Cfg.Keys.Server,
	} {
		user := srv.repos.Users.Search(k.Pub.Encr)
		if user == nil {
			user, err = srv.repos.NewUser(k.Pub.Encr)
			if err != nil {
				goto egress
			}
			user.ASN.Auth = *k.Pub.Auth
			user.ASN.Author = *k.Pub.Encr
		}
		user = nil
	}
	if len(args) > 0 {
		// local server command line exec
		ses := NewSes()
		ses.srv = srv
		ses.ASN.Repos = srv.repos
		ses.Keys.Client.Login = *srv.cmd.Cfg.Keys.Admin.Pub.Encr
		ses.asnsrv = true
		v := ses.Exec(Requester{}, cmd.In, args...)
		err, _ = v.(error)
		AckOut(cmd.Out, v)
		v = nil
	} else {
		if err = srv.Listen(); err == nil {
			cmd.In.Close()
			cmd.Out.Close()
			// FIXME should we close or os.Stderr?
			Log.Println("started", cmd.Cfg.Name,
				"with", len(srv.listeners), "listener(s)")
			for {
				sig := <-srv.cmd.Sig
				Diag.Println("caught", sig)
				if IsINT(sig) || IsTERM(sig) {
					TraceFlush(Diag)
					srv.Close()
					if IsINT(sig) {
						srv.Hangup()
						break
					}
				} else if IsUSR1(sig) {
					TraceFlush(Log)
				}
			}
			Log.Println("stopped", cmd.Cfg.Name)
		}
	}
egress:
	if err != nil {
		Log.Println("ERROR:", cmd.Cfg.Name, err)
		Diag.Println("oops", err)
	}
	cmd.Done <- err
}

func (srv *Server) AddListener(l *SrvListener) {
	srv.mutex.Lock()
	defer srv.mutex.Unlock()
	for _, p := range srv.listeners {
		if p == nil {
			p = l
			return
		}
	}
	srv.listeners = append(srv.listeners, l)
}

func (srv *Server) Close() {
	for i, le := range srv.listeners {
		if le.ws {
			le.ln.Close()
		} else {
			le.stop <- struct{}{}
			<-le.done
		}
		close(le.stop)
		close(le.done)
		le.ln = nil
		srv.listeners[i] = nil
	}
	srv.listeners = nil
}

func (srv *Server) ForEachSession(f func(*Ses)) {
	srv.mutex.Lock()
	defer srv.mutex.Unlock()
	for _, ses := range srv.sessions {
		f(ses)
	}
}

func (srv *Server) Free(ses *Ses) {
	ses.ExecMutex.Lock()
	ses.ExecMutex.Unlock()
	srv.mutex.Lock()
	defer srv.mutex.Unlock()
	ses.Free()
	for i := range srv.sessions {
		if srv.sessions[i] == ses {
			copy(srv.sessions[i:], srv.sessions[i+1:])
			srv.sessions[len(srv.sessions)-1] = nil
			srv.sessions = srv.sessions[:len(srv.sessions)-1]
			break
		}
	}
}

func (srv *Server) handler(conn net.Conn) {
	ses := srv.newSes()
	defer func() { srv.Free(ses) }()
	ses.ASN.SetConn(conn)
	conn.Read(ses.Keys.Client.Ephemeral[:])
	ses.ASN.Println("connected",
		ses.Keys.Client.Ephemeral.String()[:8]+"...")
	ses.ASN.SetBox(NewBox(2, srv.cmd.Cfg.Keys.Nonce,
		&ses.Keys.Client.Ephemeral,
		srv.cmd.Cfg.Keys.Server.Pub.Encr,
		srv.cmd.Cfg.Keys.Server.Sec.Encr))
	for {
		pdu := <-ses.ASN.RxQ
		if pdu == nil {
			break
		}
		err := pdu.Open()
		if err != nil {
			pdu.Free()
			break
		}
		var (
			v  Version
			id Id
		)
		v.ReadFrom(pdu)
		if v > ses.ASN.Version() {
			ses.ASN.SetVersion(v)
		}
		id.ReadFrom(pdu)
		id.Internal(v)
		ses.ASN.Time.Out = time.Now()
		switch id {
		case AckReqId:
			err = ses.ASN.AckerRx(pdu)
		case ExecReqId:
			err = ses.RxExec(pdu)
		case LoginReqId:
			err = ses.RxLogin(pdu)
		case PauseReqId:
			err = ses.RxPause(pdu)
		case ResumeReqId:
			err = ses.RxResume(pdu)
		case QuitReqId:
			err = ses.RxQuit(pdu)
		case BlobId:
			err = ses.RxBlob(pdu)
		default:
			if id >= Nids {
				err = ErrIncompatible
			} else {
				err = ErrUnsupported
			}
		}
		pdu.Free()
		pdu = nil
		if err != nil {
			ses.ASN.Println("Error:", err)
			break
		}
	}
}

func (srv *Server) Hangup() {
	for len(srv.sessions) > 0 {
		srv.mutex.Lock()
		ses := srv.sessions[0]
		srv.mutex.Unlock()
		ses.ASN.SetStateClosed()
		for {
			time.Sleep(100 * time.Millisecond)
			if len(srv.sessions) == 0 || srv.sessions[0] != ses {
				break
			}
		}
	}
	srv.sessions = nil
}

func (srv *Server) Listen() error {
	for _, lurl := range srv.cmd.Cfg.Listen {
		l := &SrvListener{
			stop: make(chan struct{}, 1),
			done: make(chan error, 1),
		}
		switch lurl.Scheme {
		case "tcp":
			addr, err := net.ResolveTCPAddr(lurl.Scheme, lurl.Host)
			if err != nil {
				return err
			}
			l.ln, err = net.ListenTCP(lurl.Scheme, addr)
			if err != nil {
				return err
			}
			srv.AddListener(l)
			Diag.Println(srv.cmd.Cfg.Name, "listening on", addr)
			go l.listen(srv)
		case "unix":
			path := UrlPathSearch(lurl.Path)
			os.Remove(path)
			addr, err := net.ResolveUnixAddr(lurl.Scheme, path)
			if err != nil {
				return err
			}
			l.ln, err = net.ListenUnix(lurl.Scheme, addr)
			if err != nil {
				return err
			}
			srv.AddListener(l)
			l.clean = path
			Diag.Println(srv.cmd.Cfg.Name, "listening on", addr)
			go l.listen(srv)
		case "ws":
			l.ws = true
			addr, err := net.ResolveTCPAddr("tcp", lurl.Host)
			if err != nil {
				return err
			}
			if l.ln, err = net.ListenTCP("tcp", addr); err != nil {
				return err
			}
			srv.AddListener(l)
			f := func(ws *websocket.Conn) {
				srv.handler(ws)
			}
			/*
				FIXME should use a custom handler
				h := func (w http.ResponseWriter, req *http.Request) {
					s := websocket.Server{Handler: websocket.Handler(webHandler)}
					s.ServeHTTP(w, req)
				});
				s := &http.Server{
					Addr:           ":8080",
					Handler:        h,
					ReadTimeout:    10 * time.Second,
					WriteTimeout:   10 * time.Second,
					MaxHeaderBytes: 1 << 20,
				}
				return s.Serve(l)
			*/
			/*
				http.Handle(lurl.Path, websocket.Handler(f))
				go http.Serve(l.ln, nil)
			*/
			mux := http.NewServeMux()
			mux.Handle(lurl.Path, websocket.Handler(f))
			Diag.Println(srv.cmd.Cfg.Name, "listening on", addr)
			go http.Serve(l.ln, mux)
		default:
			Log.Println("lurl:", lurl.String())
			return errors.New("unsupported scheme: " + lurl.Scheme)
		}
	}
	return nil
}

func (srv *Server) newSes() (ses *Ses) {
	srv.mutex.Lock()
	ses = NewSes()
	srv.sessions = append(srv.sessions, ses)
	ses.srv = srv
	ses.ASN.Repos = srv.repos
	ses.ASN.Name.Local = srv.cmd.Cfg.Name
	ses.ASN.Name.Remote = "unnamed"
	ses.ASN.Name.Session = ses.ASN.Name.Local + ":" + ses.ASN.Name.Remote
	srv.mutex.Unlock()
	return
}

func (l *SrvListener) listen(srv *Server) {
	for {
		select {
		case <-l.stop:
			err := l.ln.Close()
			if len(l.clean) > 0 {
				os.Remove(l.clean)
			}
			l.done <- err
			return
		default:
			l.ln.SetDeadline(time.Now().Add(ldl))
			conn, err := l.ln.Accept()
			if err == nil {
				go srv.handler(conn)
			} else if opErr, ok := err.(*net.OpError); !ok ||
				!opErr.Timeout() {
				Diag.Println("accept", err)
			}
		}
	}
}
<|endoftext|>


<commit_before>package anaconda_test

import (
	"fmt"
	"github.com/ChimeraCoder/anaconda"
	"os"
	"reflect"
	"testing"
	"time"
)

var CONSUMER_KEY = os.Getenv("CONSUMER_KEY")
var CONSUMER_SECRET = os.Getenv("CONSUMER_SECRET")
var ACCESS_TOKEN = os.Getenv("ACCESS_TOKEN")
var ACCESS_TOKEN_SECRET = os.Getenv("ACCESS_TOKEN_SECRET")

var api *anaconda.TwitterApi

func init() {
	// Initialize api so it can be used even when invidual tests are run in isolation
	anaconda.SetConsumerKey(CONSUMER_KEY)
	anaconda.SetConsumerSecret(CONSUMER_SECRET)
	api = anaconda.NewTwitterApi(ACCESS_TOKEN, ACCESS_TOKEN_SECRET)
}

// Test_TwitterCredentials tests that non-empty Twitter credentials are set
// Without this, all following tests will fail
func Test_TwitterCredentials(t *testing.T) {
	if CONSUMER_KEY == "" || CONSUMER_SECRET == "" || ACCESS_TOKEN == "" || ACCESS_TOKEN_SECRET == "" {
		t.Errorf("Credentials are invalid: at least one is empty")
	}
}

// Test that creating a TwitterApi client creates a client with non-empty OAuth credentials
func Test_TwitterApi_NewTwitterApi(t *testing.T) {
	anaconda.SetConsumerKey(CONSUMER_KEY)
	anaconda.SetConsumerSecret(CONSUMER_SECRET)
	api = anaconda.NewTwitterApi(ACCESS_TOKEN, ACCESS_TOKEN_SECRET)

	if api.Credentials == nil {
		t.Errorf("Twitter Api client has empty (nil) credentials")
	}
}

// Test that the GetSearch function actually works and returns non-empty results
func Test_TwitterApi_GetSearch(t *testing.T) {
	search_result, err := api.GetSearch("golang", nil)
	if err != nil {
		t.Errorf("GetSearch yielded error %s", err.Error())
		panic(err)
	}

	// Unless something is seriously wrong, there should be at least two tweets
	if len(search_result) < 2 {
		t.Errorf("Expected 2 or more tweets, and found %d", len(search_result))
	}

	// Check that at least one tweet is non-empty
	for _, tweet := range search_result {
		if tweet.Text != "" {
			return
		}
		fmt.Print(tweet.Text)
	}

	t.Errorf("All %d tweets had empty text", len(search_result))
}

// Test that a valid user can be fetched
// and that unmarshalling works properly
func Test_GetUser(t *testing.T) {
	const username = "chimeracoder"

	users, err := api.GetUsersLookup(username, nil)
	if err != nil {
		t.Errorf("GetUsersLookup returned error: %s", err.Error())
	}

	if len(users) != 1 {
		t.Errorf("Expected one user and received %d", len(users))
	}

	// If all attributes are equal to the zero value for that type,
	// then the original value was not valid
	if reflect.DeepEqual(users[0], anaconda.User{}) {
		t.Errorf("Received %#v", users[0])
	}

}

// Test that a valid tweet can be fetched properly
// and that unmarshalling of tweet works without error
func Test_GetTweet(t *testing.T) {
	const tweetId = 303777106620452864
	const tweetText = `golang-syd is in session. Dave Symonds is now talking about API design and protobufs. #golang http://t.co/eSq3ROwu`

	tweet, err := api.GetTweet(tweetId, nil)
	if err != nil {
		t.Errorf("GetTweet returned error: %s", err.Error())
	}

	if tweet.Text != tweetText {
		t.Errorf("Tweet %d contained incorrect text. Received: %s", tweetId, tweetText)
	}

	// Check the entities
	expectedEntities := anaconda.Entities{Hashtags: []struct {
		Indices []int
		Text    string
	}{struct {
		Indices []int
		Text    string
	}{Indices: []int{86, 93}, Text: "golang"}}, Urls: []struct {
		Indices      []int
		Url          string
		Display_url  string
		Expanded_url string
	}{}, User_mentions: []struct {
		Name        string
		Indices     []int
		Screen_name string
		Id          int64
		Id_str      string
	}{}, Media: []struct {
		Id              int64
		Id_str          string
		Media_url       string
		Media_url_https string
		Url             string
		Display_url     string
		Expanded_url    string
		Sizes           anaconda.MediaSizes
		Type            string
		Indices         []int
	}{struct {
		Id              int64
		Id_str          string
		Media_url       string
		Media_url_https string
		Url             string
		Display_url     string
		Expanded_url    string
		Sizes           anaconda.MediaSizes
		Type            string
		Indices         []int
	}{Id: 303777106628841472, Id_str: "303777106628841472", Media_url: "http://pbs.twimg.com/media/BDc7q0OCEAAoe2C.jpg", Media_url_https: "https://pbs.twimg.com/media/BDc7q0OCEAAoe2C.jpg", Url: "http://t.co/eSq3ROwu", Display_url: "pic.twitter.com/eSq3ROwu", Expanded_url: "http://twitter.com/golang/status/303777106620452864/photo/1", Sizes: anaconda.MediaSizes{Medium: anaconda.MediaSize{W: 600, H: 450, Resize: "fit"}, Thumb: anaconda.MediaSize{W: 150, H: 150, Resize: "crop"}, Small: anaconda.MediaSize{W: 340, H: 255, Resize: "fit"}, Large: anaconda.MediaSize{W: 1024, H: 768, Resize: "fit"}}, Type: "photo", Indices: []int{94, 114}}}}
	if !reflect.DeepEqual(tweet.Entities, expectedEntities) {
		t.Errorf("Tweet entities differ")
	}

}

// This assumes that the current user has at least two pages' worth of followers
func Test_GetFollowersListAll(t *testing.T) {
	result := api.GetFollowersListAll(nil)
	i := 0

	for page := range result {
		if i == 2 {
			return
		}

		if page.Error != nil {
			t.Errorf("Receved error from GetFollowersListAll: %s", page.Error)
		}

		if page.Followers == nil || len(page.Followers) == 0 {
			t.Errorf("Received invalid value for page %d of followers: %v", i, page.Followers)
		}
		i++
	}
}

// Test that setting the delay actually changes the stored delay value
func Test_TwitterApi_SetDelay(t *testing.T) {
	const OLD_DELAY = 1 * time.Second
	const NEW_DELAY = 20 * time.Second
	api.EnableThrottling(OLD_DELAY, 4)

	delay := api.GetDelay()
	if delay != OLD_DELAY {
		t.Errorf("Expected initial delay to be the default delay (%s)", anaconda.DEFAULT_DELAY.String())
	}

	api.SetDelay(NEW_DELAY)

	if newDelay := api.GetDelay(); newDelay != NEW_DELAY {
		t.Errorf("Attempted to set delay to %s, but delay is now %s (original delay: %s)", NEW_DELAY, newDelay, delay)
	}
}

func Test_TwitterApi_TwitterErrorDoesNotExist(t *testing.T) {

	// Try fetching a tweet that no longer exists (was deleted)
	const DELETED_TWEET_ID = 404409873170841600

	tweet, err := api.GetTweet(DELETED_TWEET_ID, nil)
	if err == nil {
		t.Errorf("Expected an error when fetching tweet with id %d but got none - tweet object is %+v", DELETED_TWEET_ID, tweet)
	}

	apiErr, ok := err.(*anaconda.ApiError)
	if !ok {
		t.Errorf("Expected an *anaconda.ApiError, and received error message %s, (%+v)", err.Error(), err)
	}

	terr, ok := apiErr.Decoded.First().(anaconda.TwitterError)

	if !ok {
		t.Errorf("TwitterErrorResponse.First() should return value of type TwitterError, not %s", reflect.TypeOf(apiErr.Decoded.First()))
	}

	if code := terr.Code; code != anaconda.TwitterErrorDoesNotExist {
		if code == anaconda.TwitterErrorRateLimitExceeded {
			t.Errorf("Rate limit exceeded during testing - received error code %d instead of %d", anaconda.TwitterErrorRateLimitExceeded, anaconda.TwitterErrorDoesNotExist)
		} else {

			t.Errorf("Expected Twitter to return error code %d, and instead received error code %d", anaconda.TwitterErrorDoesNotExist, code)
		}
	}
}

// Test that the client can be used to throttle to an arbitrary duration
func Test_TwitterApi_Throttling(t *testing.T) {
	const MIN_DELAY = 15 * time.Second

	api.EnableThrottling(MIN_DELAY, 5)
	oldDelay := api.GetDelay()
	api.SetDelay(MIN_DELAY)

	now := time.Now()
	_, err := api.GetSearch("golang", nil)
	if err != nil {
		t.Errorf("GetSearch yielded error %s", err.Error())
	}
	_, err = api.GetSearch("anaconda", nil)
	if err != nil {
		t.Errorf("GetSearch yielded error %s", err.Error())
	}
	after := time.Now()

	if difference := after.Sub(now); difference < MIN_DELAY {
		t.Errorf("Expected delay of at least %d. Actual delay: %s", MIN_DELAY.String(), difference.String())
	}

	// Reset the delay to its previous value
	api.SetDelay(oldDelay)
}
<commit_msg>Add unit test for GetFavorites<commit_after>package anaconda_test

import (
	"fmt"
	"github.com/ChimeraCoder/anaconda"
	"net/url"
	"os"
	"reflect"
	"testing"
	"time"
)

var CONSUMER_KEY = os.Getenv("CONSUMER_KEY")
var CONSUMER_SECRET = os.Getenv("CONSUMER_SECRET")
var ACCESS_TOKEN = os.Getenv("ACCESS_TOKEN")
var ACCESS_TOKEN_SECRET = os.Getenv("ACCESS_TOKEN_SECRET")

var api *anaconda.TwitterApi

func init() {
	// Initialize api so it can be used even when invidual tests are run in isolation
	anaconda.SetConsumerKey(CONSUMER_KEY)
	anaconda.SetConsumerSecret(CONSUMER_SECRET)
	api = anaconda.NewTwitterApi(ACCESS_TOKEN, ACCESS_TOKEN_SECRET)
}

// Test_TwitterCredentials tests that non-empty Twitter credentials are set
// Without this, all following tests will fail
func Test_TwitterCredentials(t *testing.T) {
	if CONSUMER_KEY == "" || CONSUMER_SECRET == "" || ACCESS_TOKEN == "" || ACCESS_TOKEN_SECRET == "" {
		t.Errorf("Credentials are invalid: at least one is empty")
	}
}

// Test that creating a TwitterApi client creates a client with non-empty OAuth credentials
func Test_TwitterApi_NewTwitterApi(t *testing.T) {
	anaconda.SetConsumerKey(CONSUMER_KEY)
	anaconda.SetConsumerSecret(CONSUMER_SECRET)
	api = anaconda.NewTwitterApi(ACCESS_TOKEN, ACCESS_TOKEN_SECRET)

	if api.Credentials == nil {
		t.Errorf("Twitter Api client has empty (nil) credentials")
	}
}

// Test that the GetSearch function actually works and returns non-empty results
func Test_TwitterApi_GetSearch(t *testing.T) {
	search_result, err := api.GetSearch("golang", nil)
	if err != nil {
		t.Errorf("GetSearch yielded error %s", err.Error())
		panic(err)
	}

	// Unless something is seriously wrong, there should be at least two tweets
	if len(search_result) < 2 {
		t.Errorf("Expected 2 or more tweets, and found %d", len(search_result))
	}

	// Check that at least one tweet is non-empty
	for _, tweet := range search_result {
		if tweet.Text != "" {
			return
		}
		fmt.Print(tweet.Text)
	}

	t.Errorf("All %d tweets had empty text", len(search_result))
}

// Test that a valid user can be fetched
// and that unmarshalling works properly
func Test_GetUser(t *testing.T) {
	const username = "chimeracoder"

	users, err := api.GetUsersLookup(username, nil)
	if err != nil {
		t.Errorf("GetUsersLookup returned error: %s", err.Error())
	}

	if len(users) != 1 {
		t.Errorf("Expected one user and received %d", len(users))
	}

	// If all attributes are equal to the zero value for that type,
	// then the original value was not valid
	if reflect.DeepEqual(users[0], anaconda.User{}) {
		t.Errorf("Received %#v", users[0])
	}
}

func Test_GetFavorites(t *testing.T) {
	v := url.Values{}
	v.Set("screen_name", "chimeracoder")
	favorites, err := api.GetFavorites(v)
	if err != nil {
		t.Errorf("GetFavorites returned error: %s", err.Error())
	}

	if len(favorites) == 0 {
		t.Errorf("GetFavorites returned no favorites")
	}

	if reflect.DeepEqual(favorites[0], anaconda.Tweet{}) {
		t.Errorf("GetFavorites returned %d favorites and the first one was empty", len(favorites))
	}
}

// Test that a valid tweet can be fetched properly
// and that unmarshalling of tweet works without error
func Test_GetTweet(t *testing.T) {
	const tweetId = 303777106620452864
	const tweetText = `golang-syd is in session. Dave Symonds is now talking about API design and protobufs. #golang http://t.co/eSq3ROwu`

	tweet, err := api.GetTweet(tweetId, nil)
	if err != nil {
		t.Errorf("GetTweet returned error: %s", err.Error())
	}

	if tweet.Text != tweetText {
		t.Errorf("Tweet %d contained incorrect text. Received: %s", tweetId, tweetText)
	}

	// Check the entities
	expectedEntities := anaconda.Entities{Hashtags: []struct {
		Indices []int
		Text    string
	}{struct {
		Indices []int
		Text    string
	}{Indices: []int{86, 93}, Text: "golang"}}, Urls: []struct {
		Indices      []int
		Url          string
		Display_url  string
		Expanded_url string
	}{}, User_mentions: []struct {
		Name        string
		Indices     []int
		Screen_name string
		Id          int64
		Id_str      string
	}{}, Media: []struct {
		Id              int64
		Id_str          string
		Media_url       string
		Media_url_https string
		Url             string
		Display_url     string
		Expanded_url    string
		Sizes           anaconda.MediaSizes
		Type            string
		Indices         []int
	}{struct {
		Id              int64
		Id_str          string
		Media_url       string
		Media_url_https string
		Url             string
		Display_url     string
		Expanded_url    string
		Sizes           anaconda.MediaSizes
		Type            string
		Indices         []int
	}{Id: 303777106628841472, Id_str: "303777106628841472", Media_url: "http://pbs.twimg.com/media/BDc7q0OCEAAoe2C.jpg", Media_url_https: "https://pbs.twimg.com/media/BDc7q0OCEAAoe2C.jpg", Url: "http://t.co/eSq3ROwu", Display_url: "pic.twitter.com/eSq3ROwu", Expanded_url: "http://twitter.com/golang/status/303777106620452864/photo/1", Sizes: anaconda.MediaSizes{Medium: anaconda.MediaSize{W: 600, H: 450, Resize: "fit"}, Thumb: anaconda.MediaSize{W: 150, H: 150, Resize: "crop"}, Small: anaconda.MediaSize{W: 340, H: 255, Resize: "fit"}, Large: anaconda.MediaSize{W: 1024, H: 768, Resize: "fit"}}, Type: "photo", Indices: []int{94, 114}}}}
	if !reflect.DeepEqual(tweet.Entities, expectedEntities) {
		t.Errorf("Tweet entities differ")
	}

}

// This assumes that the current user has at least two pages' worth of followers
func Test_GetFollowersListAll(t *testing.T) {
	result := api.GetFollowersListAll(nil)
	i := 0

	for page := range result {
		if i == 2 {
			return
		}

		if page.Error != nil {
			t.Errorf("Receved error from GetFollowersListAll: %s", page.Error)
		}

		if page.Followers == nil || len(page.Followers) == 0 {
			t.Errorf("Received invalid value for page %d of followers: %v", i, page.Followers)
		}
		i++
	}
}

// Test that setting the delay actually changes the stored delay value
func Test_TwitterApi_SetDelay(t *testing.T) {
	const OLD_DELAY = 1 * time.Second
	const NEW_DELAY = 20 * time.Second
	api.EnableThrottling(OLD_DELAY, 4)

	delay := api.GetDelay()
	if delay != OLD_DELAY {
		t.Errorf("Expected initial delay to be the default delay (%s)", anaconda.DEFAULT_DELAY.String())
	}

	api.SetDelay(NEW_DELAY)

	if newDelay := api.GetDelay(); newDelay != NEW_DELAY {
		t.Errorf("Attempted to set delay to %s, but delay is now %s (original delay: %s)", NEW_DELAY, newDelay, delay)
	}
}

func Test_TwitterApi_TwitterErrorDoesNotExist(t *testing.T) {

	// Try fetching a tweet that no longer exists (was deleted)
	const DELETED_TWEET_ID = 404409873170841600

	tweet, err := api.GetTweet(DELETED_TWEET_ID, nil)
	if err == nil {
		t.Errorf("Expected an error when fetching tweet with id %d but got none - tweet object is %+v", DELETED_TWEET_ID, tweet)
	}

	apiErr, ok := err.(*anaconda.ApiError)
	if !ok {
		t.Errorf("Expected an *anaconda.ApiError, and received error message %s, (%+v)", err.Error(), err)
	}

	terr, ok := apiErr.Decoded.First().(anaconda.TwitterError)

	if !ok {
		t.Errorf("TwitterErrorResponse.First() should return value of type TwitterError, not %s", reflect.TypeOf(apiErr.Decoded.First()))
	}

	if code := terr.Code; code != anaconda.TwitterErrorDoesNotExist {
		if code == anaconda.TwitterErrorRateLimitExceeded {
			t.Errorf("Rate limit exceeded during testing - received error code %d instead of %d", anaconda.TwitterErrorRateLimitExceeded, anaconda.TwitterErrorDoesNotExist)
		} else {

			t.Errorf("Expected Twitter to return error code %d, and instead received error code %d", anaconda.TwitterErrorDoesNotExist, code)
		}
	}
}

// Test that the client can be used to throttle to an arbitrary duration
func Test_TwitterApi_Throttling(t *testing.T) {
	const MIN_DELAY = 15 * time.Second

	api.EnableThrottling(MIN_DELAY, 5)
	oldDelay := api.GetDelay()
	api.SetDelay(MIN_DELAY)

	now := time.Now()
	_, err := api.GetSearch("golang", nil)
	if err != nil {
		t.Errorf("GetSearch yielded error %s", err.Error())
	}
	_, err = api.GetSearch("anaconda", nil)
	if err != nil {
		t.Errorf("GetSearch yielded error %s", err.Error())
	}
	after := time.Now()

	if difference := after.Sub(now); difference < MIN_DELAY {
		t.Errorf("Expected delay of at least %d. Actual delay: %s", MIN_DELAY.String(), difference.String())
	}

	// Reset the delay to its previous value
	api.SetDelay(oldDelay)
}
<|endoftext|>


<commit_before>package main

import tui "github.com/gizak/termui"

type TreeItem struct {
	node    *DepsNode
	parent  *TreeItem
	sibling *TreeItem
	child   *TreeItem // pointer to first child
	folded  bool
	total   int // number of (shown) children (not count itself)
}

type TreeView struct {
	tui.Block // embedded
	Root      *TreeItem
	Curr      *TreeItem

	ItemFgColor  tui.Attribute
	ItemBgColor  tui.Attribute
	FocusFgColor tui.Attribute
	FocusBgColor tui.Attribute

	idx int // current cursor position
	off int // first entry displayed
}

func NewTreeView() *TreeView {
	tv := &TreeView{Block: *tui.NewBlock()}

	tv.ItemFgColor = tui.ThemeAttr("list.item.fg")
	tv.ItemBgColor = tui.ThemeAttr("list.item.bg")
	tv.FocusFgColor = tui.ColorYellow
	tv.FocusBgColor = tui.ColorBlue

	tv.idx = 0
	tv.off = 0
	return tv
}

func (ti *TreeItem) next() *TreeItem {
	if ti.child == nil || ti.folded {
		for ti != nil {
			if ti.sibling != nil {
				return ti.sibling
			}

			ti = ti.parent
		}
		return nil
	}
	return ti.child
}

func (ti *TreeItem) expand() {
	if !ti.folded || ti.child == nil {
		return
	}

	for c := ti.child; c != nil; c = c.sibling {
		ti.total += c.total + 1
	}

	for p := ti.parent; p != nil; p = p.parent {
		p.total += ti.total
	}

	ti.folded = false
}

func (ti *TreeItem) fold() {
	if ti.folded || ti.child == nil {
		return
	}

	for p := ti.parent; p != nil; p = p.parent {
		p.total -= ti.total
	}
	ti.total = 0

	ti.folded = true
}

func (ti *TreeItem) toggle() {
	if ti.folded {
		ti.expand()
	} else {
		ti.fold()
	}
}

// Buffer implements Bufferer interface.
func (tv *TreeView) Buffer() tui.Buffer {
	buf := tv.Block.Buffer()

	i := 0
	printed := 0

	var ti *TreeItem
	for ti = tv.Root; ti != nil; ti = ti.next() {
		if i < tv.off {
			i++
			continue
		}
		if printed == tv.Height-2 {
			break
		}

		fg := tv.ItemFgColor
		bg := tv.ItemBgColor
		if i == tv.idx {
			fg = tv.FocusFgColor
			bg = tv.FocusBgColor

			tv.Curr = ti
		}

		indent := 3 * ti.node.depth
		cs := tui.DefaultTxBuilder.Build(ti.node.name, fg, bg)
		cs = tui.DTrimTxCls(cs, (tv.Width-2)-2-indent)

		j := 0
		if i == tv.idx {
			// draw current line cursor from the beginning
			for j < indent {
				buf.Set(j+1, printed+1, tui.Cell{' ', fg, bg})
				j++
			}
		} else {
			j = indent
		}

		if ti.folded {
			buf.Set(j+1, printed+1, tui.Cell{'+', fg, bg})
		} else {
			buf.Set(j+1, printed+1, tui.Cell{'-', fg, bg})
		}
		buf.Set(j+2, printed+1, tui.Cell{' ', fg, bg})
		j += 2

		for _, vv := range cs {
			w := vv.Width()
			buf.Set(j+1, printed+1, vv)
			j += w
		}

		printed++
		i++

		if i != tv.idx+1 {
			continue
		}

		// draw current line cursor to the end
		for j < tv.Width-2 {
			buf.Set(j+1, printed, tui.Cell{' ', fg, bg})
			j++
		}
	}
	return buf
}

func (tv *TreeView) Down() {
	if tv.idx < tv.Root.total {
		tv.idx++
	}
	if tv.idx-tv.off >= tv.Height-2 {
		tv.off++
	}
}

func (tv *TreeView) Up() {
	if tv.idx > 0 {
		tv.idx--
	}
	if tv.idx < tv.off {
		tv.off = tv.idx
	}
}

func (tv *TreeView) PageDown() {
	bottom := tv.off + (tv.Height - 2) - 1
	if bottom > tv.Root.total {
		bottom = tv.Root.total
	}

	// At first, move to the bottom of current page
	if tv.idx != bottom {
		tv.idx = bottom
		return
	}

	tv.idx += tv.Height - 2
	if tv.idx > tv.Root.total {
		tv.idx = tv.Root.total
	}
	if tv.idx-tv.off >= tv.Height-2 {
		tv.off = tv.idx - (tv.Height - 2) + 1
	}
}

func (tv *TreeView) PageUp() {
	// At first, move to the top of current page
	if tv.idx != tv.off {
		tv.idx = tv.off
		return
	}

	tv.idx -= tv.Height - 2
	if tv.idx < 0 {
		tv.idx = 0
	}

	tv.off = tv.idx
}

func (tv *TreeView) Home() {
	tv.idx = 0
	tv.off = 0
}

func (tv *TreeView) End() {
	tv.idx = tv.Root.total
	tv.off = tv.idx - (tv.Height - 2) + 1

	if tv.off < 0 {
		tv.off = 0
	}
}

func (tv *TreeView) Toggle() {
	tv.Curr.toggle()
}

func makeItems(dep *DepsNode, parent *TreeItem) *TreeItem {
	item := &TreeItem{node: dep, parent: parent, folded: false, total: len(dep.child)}

	var prev *TreeItem
	for _, v := range dep.child {
		c := makeItems(v, item)

		if item.child == nil {
			item.child = c
		}
		if prev != nil {
			prev.sibling = c
		}
		prev = c

		item.total += c.total
	}
	return item
}

func ShowWithTUI(dep *DepsNode) {
	if err := tui.Init(); err != nil {
		panic(err)
	}
	defer tui.Close()

	root := makeItems(dep, nil)

	tv := NewTreeView()

	tv.BorderLabel = "ELF Tree"
	tv.Height = tui.TermHeight()
	tv.Width = tui.TermWidth()
	tv.Root = root
	tv.Curr = root

	tui.Render(tv)

	// handle key pressing
	tui.Handle("/sys/kbd/q", func(tui.Event) {
		// press q to quit
		tui.StopLoop()
	})
	tui.Handle("/sys/kbd/C-c", func(tui.Event) {
		// press Ctrl-C to quit
		tui.StopLoop()
	})

	tui.Handle("/sys/kbd/<down>", func(tui.Event) {
		tv.Down()
		tui.Render(tv)
	})
	tui.Handle("/sys/kbd/<up>", func(tui.Event) {
		tv.Up()
		tui.Render(tv)
	})
	tui.Handle("/sys/kbd/<next>", func(tui.Event) {
		tv.PageDown()
		tui.Render(tv)
	})
	tui.Handle("/sys/kbd/<previous>", func(tui.Event) {
		tv.PageUp()
		tui.Render(tv)
	})
	tui.Handle("/sys/kbd/<home>", func(tui.Event) {
		tv.Home()
		tui.Render(tv)
	})
	tui.Handle("/sys/kbd/<end>", func(tui.Event) {
		tv.End()
		tui.Render(tv)
	})

	tui.Handle("/sys/kbd/<enter>", func(tui.Event) {
		tv.Toggle()
		tui.Render(tv)
	})

	tui.Handle("/sys/wnd/resize", func(tui.Event) {
		tv.Height = tui.TermHeight()
		tv.Width = tui.TermWidth()
		tui.Render(tv)
	})

	tui.Loop()
}
<commit_msg>Introduce rows and cols in TreeView struct<commit_after>package main

import tui "github.com/gizak/termui"

type TreeItem struct {
	node    *DepsNode
	parent  *TreeItem
	sibling *TreeItem
	child   *TreeItem // pointer to first child
	folded  bool
	total   int // number of (shown) children (not count itself)
}

type TreeView struct {
	tui.Block // embedded
	Root      *TreeItem
	Curr      *TreeItem

	ItemFgColor  tui.Attribute
	ItemBgColor  tui.Attribute
	FocusFgColor tui.Attribute
	FocusBgColor tui.Attribute

	idx int // current cursor position
	off int // first entry displayed

	rows int
	cols int
}

func NewTreeView() *TreeView {
	tv := &TreeView{Block: *tui.NewBlock()}

	tv.ItemFgColor = tui.ThemeAttr("list.item.fg")
	tv.ItemBgColor = tui.ThemeAttr("list.item.bg")
	tv.FocusFgColor = tui.ColorYellow
	tv.FocusBgColor = tui.ColorBlue

	tv.idx = 0
	tv.off = 0
	return tv
}

func (ti *TreeItem) next() *TreeItem {
	if ti.child == nil || ti.folded {
		for ti != nil {
			if ti.sibling != nil {
				return ti.sibling
			}

			ti = ti.parent
		}
		return nil
	}
	return ti.child
}

func (ti *TreeItem) expand() {
	if !ti.folded || ti.child == nil {
		return
	}

	for c := ti.child; c != nil; c = c.sibling {
		ti.total += c.total + 1
	}

	for p := ti.parent; p != nil; p = p.parent {
		p.total += ti.total
	}

	ti.folded = false
}

func (ti *TreeItem) fold() {
	if ti.folded || ti.child == nil {
		return
	}

	for p := ti.parent; p != nil; p = p.parent {
		p.total -= ti.total
	}
	ti.total = 0

	ti.folded = true
}

func (ti *TreeItem) toggle() {
	if ti.folded {
		ti.expand()
	} else {
		ti.fold()
	}
}

// Buffer implements Bufferer interface.
func (tv *TreeView) Buffer() tui.Buffer {
	buf := tv.Block.Buffer()

	i := 0
	printed := 0

	var ti *TreeItem
	for ti = tv.Root; ti != nil; ti = ti.next() {
		if i < tv.off {
			i++
			continue
		}
		if printed == tv.rows {
			break
		}

		fg := tv.ItemFgColor
		bg := tv.ItemBgColor
		if i == tv.idx {
			fg = tv.FocusFgColor
			bg = tv.FocusBgColor

			tv.Curr = ti
		}

		indent := 3 * ti.node.depth
		cs := tui.DefaultTxBuilder.Build(ti.node.name, fg, bg)
		cs = tui.DTrimTxCls(cs, tv.cols-2-indent)

		j := 0
		if i == tv.idx {
			// draw current line cursor from the beginning
			for j < indent {
				buf.Set(j+1, printed+1, tui.Cell{' ', fg, bg})
				j++
			}
		} else {
			j = indent
		}

		if ti.folded {
			buf.Set(j+1, printed+1, tui.Cell{'+', fg, bg})
		} else {
			buf.Set(j+1, printed+1, tui.Cell{'-', fg, bg})
		}
		buf.Set(j+2, printed+1, tui.Cell{' ', fg, bg})
		j += 2

		for _, vv := range cs {
			w := vv.Width()
			buf.Set(j+1, printed+1, vv)
			j += w
		}

		printed++
		i++

		if i != tv.idx+1 {
			continue
		}

		// draw current line cursor to the end
		for j < tv.cols {
			buf.Set(j+1, printed, tui.Cell{' ', fg, bg})
			j++
		}
	}
	return buf
}

func (tv *TreeView) Down() {
	if tv.idx < tv.Root.total {
		tv.idx++
	}
	if tv.idx-tv.off >= tv.rows {
		tv.off++
	}
}

func (tv *TreeView) Up() {
	if tv.idx > 0 {
		tv.idx--
	}
	if tv.idx < tv.off {
		tv.off = tv.idx
	}
}

func (tv *TreeView) PageDown() {
	bottom := tv.off + tv.rows - 1
	if bottom > tv.Root.total {
		bottom = tv.Root.total
	}

	// At first, move to the bottom of current page
	if tv.idx != bottom {
		tv.idx = bottom
		return
	}

	tv.idx += tv.rows
	if tv.idx > tv.Root.total {
		tv.idx = tv.Root.total
	}
	if tv.idx-tv.off >= tv.rows {
		tv.off = tv.idx - tv.rows + 1
	}
}

func (tv *TreeView) PageUp() {
	// At first, move to the top of current page
	if tv.idx != tv.off {
		tv.idx = tv.off
		return
	}

	tv.idx -= tv.rows
	if tv.idx < 0 {
		tv.idx = 0
	}

	tv.off = tv.idx
}

func (tv *TreeView) Home() {
	tv.idx = 0
	tv.off = 0
}

func (tv *TreeView) End() {
	tv.idx = tv.Root.total
	tv.off = tv.idx - tv.rows + 1

	if tv.off < 0 {
		tv.off = 0
	}
}

func (tv *TreeView) Toggle() {
	tv.Curr.toggle()
}

func makeItems(dep *DepsNode, parent *TreeItem) *TreeItem {
	item := &TreeItem{node: dep, parent: parent, folded: false, total: len(dep.child)}

	var prev *TreeItem
	for _, v := range dep.child {
		c := makeItems(v, item)

		if item.child == nil {
			item.child = c
		}
		if prev != nil {
			prev.sibling = c
		}
		prev = c

		item.total += c.total
	}
	return item
}

func ShowWithTUI(dep *DepsNode) {
	if err := tui.Init(); err != nil {
		panic(err)
	}
	defer tui.Close()

	root := makeItems(dep, nil)

	tv := NewTreeView()

	tv.BorderLabel = "ELF Tree"
	tv.Height = tui.TermHeight()
	tv.Width = tui.TermWidth()
	tv.Root = root
	tv.Curr = root

	tv.rows = tv.Height - 2 // exclude border at top and bottom
	tv.cols = tv.Width - 2  // exclude border at left and right

	tui.Render(tv)

	// handle key pressing
	tui.Handle("/sys/kbd/q", func(tui.Event) {
		// press q to quit
		tui.StopLoop()
	})
	tui.Handle("/sys/kbd/C-c", func(tui.Event) {
		// press Ctrl-C to quit
		tui.StopLoop()
	})

	tui.Handle("/sys/kbd/<down>", func(tui.Event) {
		tv.Down()
		tui.Render(tv)
	})
	tui.Handle("/sys/kbd/<up>", func(tui.Event) {
		tv.Up()
		tui.Render(tv)
	})
	tui.Handle("/sys/kbd/<next>", func(tui.Event) {
		tv.PageDown()
		tui.Render(tv)
	})
	tui.Handle("/sys/kbd/<previous>", func(tui.Event) {
		tv.PageUp()
		tui.Render(tv)
	})
	tui.Handle("/sys/kbd/<home>", func(tui.Event) {
		tv.Home()
		tui.Render(tv)
	})
	tui.Handle("/sys/kbd/<end>", func(tui.Event) {
		tv.End()
		tui.Render(tv)
	})

	tui.Handle("/sys/kbd/<enter>", func(tui.Event) {
		tv.Toggle()
		tui.Render(tv)
	})

	tui.Handle("/sys/wnd/resize", func(tui.Event) {
		tv.Height = tui.TermHeight()
		tv.Width = tui.TermWidth()
		tv.rows = tv.Height - 2
		tv.cols = tv.Width - 2
		tui.Render(tv)
	})

	tui.Loop()
}
<|endoftext|>


<commit_before>// This is the boot2docker management utilty.
package main

import (
	"flag"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
)

// The following will be injected during the build process.
var (
	Version string
	GitSHA  string
)

// B2D reprents boot2docker config.
var B2D struct {
	VBM        string // VirtualBox management utility
	SSH        string // SSH client executable
	VM         string // virtual machine name
	Dir        string // boot2docker directory
	ISO        string // boot2docker ISO image path
	Disk       string // VM disk image path
	DiskSize   int    // VM disk image size (MB)
	Memory     int    // VM memory size (MB)
	SSHPort    int    // host SSH port (forward to port 22 in VM)
	DockerPort int    // host Docker port (forward to port 4243 in VM)
}

var usageShort = fmt.Sprintf(`Usage: %s {help|init|start|up|ssh|save|pause|stop|poweroff|reset|restart|status|info|delete|download} [<vm>]
`, os.Args[0])

var usageLong = fmt.Sprintf(`Usage: %s <command> [<vm>]

boot2docker management utility.

Commands:

    init            Create a new boot2docker VM.
    up|start|boot   Start the VM from any state.
    ssh             Login to VM.
    save|suspend    Suspend the VM (saving running state to disk).
    down|stop|halt  Gracefully shutdown the VM.
    restart         Gracefully reboot the VM.
    poweroff        Forcefully shutdown the VM (might cause disk corruption).
    reset           Forcefully reboot the VM (might cause disk corruption).
    delete          Delete the boot2docker VM and its disk image.
    download        Download the boot2docker ISO image.
    info            Display the detailed information of the VM
    status          Display the current state of the VM.

`, os.Args[0])

func getCfgDir(name string) (string, error) {
	if b2dDir := os.Getenv("BOOT2DOCKER_CFG_DIR"); b2dDir != "" {
		return b2dDir, nil
	}

	// Unix
	if home := os.Getenv("HOME"); home != "" {
		return filepath.Join(home, name), nil
	}

	// Windows
	for _, env := range []string{
		"APPDATA",
		"LOCALAPPDATA",
		"USERPROFILE", // let's try USERPROFILE only as a very last resort
	} {
		if val := os.Getenv(env); val != "" {
			return filepath.Join(val, "boot2docker"), nil
		}
	}
	// ok, we've tried everything reasonable - now let's go for CWD
	cwd, err := os.Getwd()
	if err != nil {
		return "", err
	}
	return filepath.Join(cwd, name), nil
}

// Read configuration.
func config() (err error) {

	if B2D.Dir, err = getCfgDir(".boot2docker"); err != nil {
		return fmt.Errorf("failed to get current directory: %s", err)
	}
	cfgi, err := getConfigfile()

	B2D.VBM = cfgi.Get("", "VBM", "VBoxManage")
	B2D.SSH = cfgi.Get("", "BOOT2DOCKER_SSH", "ssh")
	B2D.VM = cfgi.Get("", "VM_NAME", "boot2docker-vm")

	B2D.ISO = cfgi.Get("", "BOOT2DOCKER_ISO", filepath.Join(B2D.Dir, "boot2docker.iso"))
	B2D.Disk = cfgi.Get("", "VM_DISK", filepath.Join(B2D.Dir, "boot2docker.vmdk"))

	if B2D.DiskSize, err = strconv.Atoi(cfgi.Get("", "VM_DISK_SIZE", "20000")); err != nil {
		return fmt.Errorf("invalid VM_DISK_SIZE: %s", err)
	}
	if B2D.DiskSize <= 0 {
		return fmt.Errorf("VM_DISK_SIZE way too small")
	}
	if B2D.Memory, err = strconv.Atoi(cfgi.Get("", "VM_MEM", "1024")); err != nil {
		return fmt.Errorf("invalid VM_MEM: %s", err)
	}
	if B2D.Memory <= 0 {
		return fmt.Errorf("VM_MEM way too small")
	}
	if B2D.SSHPort, err = strconv.Atoi(cfgi.Get("", "SSH_HOST_PORT", "2022")); err != nil {
		return fmt.Errorf("invalid SSH_HOST_PORT: %s", err)
	}
	if B2D.SSHPort <= 0 {
		return fmt.Errorf("invalid SSH_HOST_PORT: must be in the range of 1--65535; got %d", B2D.SSHPort)
	}
	if B2D.DockerPort, err = strconv.Atoi(cfgi.Get("", "DOCKER_PORT", "4243")); err != nil {
		return fmt.Errorf("invalid DOCKER_PORT: %s", err)
	}
	if B2D.DockerPort <= 0 {
		return fmt.Errorf("invalid DOCKER_PORT: must be in the range of 1--65535; got %d", B2D.DockerPort)
	}

	// TODO maybe allow flags to override ENV vars?
	flag.Parse()
	if vm := flag.Arg(1); vm != "" {
		B2D.VM = vm
	}
	return
}

func run() int {
	if err := config(); err != nil {
		logf("%s", err)
		return 1
	}

	if _, err := exec.LookPath(B2D.VBM); err != nil {
		logf("failed to locate VirtualBox management utility %q", B2D.VBM)
		return 2
	}

	switch cmd := flag.Arg(0); cmd {
	case "download":
		return cmdDownload()
	case "init":
		return cmdInit()
	case "start", "up", "boot", "resume":
		return cmdStart()
	case "ssh":
		return cmdSSH()
	case "save", "suspend":
		return cmdSave()
	case "pause":
		return cmdPause()
	case "halt", "down", "stop":
		return cmdStop()
	case "poweroff":
		return cmdPoweroff()
	case "restart":
		return cmdRestart()
	case "reset":
		return cmdReset()
	case "info":
		return cmdInfo()
	case "status":
		return cmdStatus()
	case "delete":
		return cmdDelete()
	case "version":
		fmt.Println("Client version:", Version)
		fmt.Println("Git commit:", GitSHA)
		return 0
	case "help":
		logf(usageLong)
		return 0
	case "":
		logf(usageShort)
		return 0
	default:
		logf("Unknown command '%s'", cmd)
		logf(usageShort)
		return 1
	}
}

func main() {
	os.Exit(run())
}
<commit_msg>Added help message for `version` commmand.<commit_after>// This is the boot2docker management utilty.
package main

import (
	"flag"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
)

// The following will be injected during the build process.
var (
	Version string
	GitSHA  string
)

// B2D reprents boot2docker config.
var B2D struct {
	VBM        string // VirtualBox management utility
	SSH        string // SSH client executable
	VM         string // virtual machine name
	Dir        string // boot2docker directory
	ISO        string // boot2docker ISO image path
	Disk       string // VM disk image path
	DiskSize   int    // VM disk image size (MB)
	Memory     int    // VM memory size (MB)
	SSHPort    int    // host SSH port (forward to port 22 in VM)
	DockerPort int    // host Docker port (forward to port 4243 in VM)
}

var usageShort = fmt.Sprintf(`Usage: %s {help|init|start|up|ssh|save|pause|stop|poweroff|reset|restart|status|info|delete|download|version} [<vm>]
`, os.Args[0])

var usageLong = fmt.Sprintf(`Usage: %s <command> [<vm>]

boot2docker management utility.

Commands:

    init            Create a new boot2docker VM.
    up|start|boot   Start the VM from any state.
    ssh             Login to VM.
    save|suspend    Suspend the VM (saving running state to disk).
    down|stop|halt  Gracefully shutdown the VM.
    restart         Gracefully reboot the VM.
    poweroff        Forcefully shutdown the VM (might cause disk corruption).
    reset           Forcefully reboot the VM (might cause disk corruption).
    delete          Delete the boot2docker VM and its disk image.
    download        Download the boot2docker ISO image.
    info            Display the detailed information of the VM
    status          Display the current state of the VM.
	version			Display version information.

`, os.Args[0])

func getCfgDir(name string) (string, error) {
	if b2dDir := os.Getenv("BOOT2DOCKER_CFG_DIR"); b2dDir != "" {
		return b2dDir, nil
	}

	// Unix
	if home := os.Getenv("HOME"); home != "" {
		return filepath.Join(home, name), nil
	}

	// Windows
	for _, env := range []string{
		"APPDATA",
		"LOCALAPPDATA",
		"USERPROFILE", // let's try USERPROFILE only as a very last resort
	} {
		if val := os.Getenv(env); val != "" {
			return filepath.Join(val, "boot2docker"), nil
		}
	}
	// ok, we've tried everything reasonable - now let's go for CWD
	cwd, err := os.Getwd()
	if err != nil {
		return "", err
	}
	return filepath.Join(cwd, name), nil
}

// Read configuration.
func config() (err error) {

	if B2D.Dir, err = getCfgDir(".boot2docker"); err != nil {
		return fmt.Errorf("failed to get current directory: %s", err)
	}
	cfgi, err := getConfigfile()

	B2D.VBM = cfgi.Get("", "VBM", "VBoxManage")
	B2D.SSH = cfgi.Get("", "BOOT2DOCKER_SSH", "ssh")
	B2D.VM = cfgi.Get("", "VM_NAME", "boot2docker-vm")

	B2D.ISO = cfgi.Get("", "BOOT2DOCKER_ISO", filepath.Join(B2D.Dir, "boot2docker.iso"))
	B2D.Disk = cfgi.Get("", "VM_DISK", filepath.Join(B2D.Dir, "boot2docker.vmdk"))

	if B2D.DiskSize, err = strconv.Atoi(cfgi.Get("", "VM_DISK_SIZE", "20000")); err != nil {
		return fmt.Errorf("invalid VM_DISK_SIZE: %s", err)
	}
	if B2D.DiskSize <= 0 {
		return fmt.Errorf("VM_DISK_SIZE way too small")
	}
	if B2D.Memory, err = strconv.Atoi(cfgi.Get("", "VM_MEM", "1024")); err != nil {
		return fmt.Errorf("invalid VM_MEM: %s", err)
	}
	if B2D.Memory <= 0 {
		return fmt.Errorf("VM_MEM way too small")
	}
	if B2D.SSHPort, err = strconv.Atoi(cfgi.Get("", "SSH_HOST_PORT", "2022")); err != nil {
		return fmt.Errorf("invalid SSH_HOST_PORT: %s", err)
	}
	if B2D.SSHPort <= 0 {
		return fmt.Errorf("invalid SSH_HOST_PORT: must be in the range of 1--65535; got %d", B2D.SSHPort)
	}
	if B2D.DockerPort, err = strconv.Atoi(cfgi.Get("", "DOCKER_PORT", "4243")); err != nil {
		return fmt.Errorf("invalid DOCKER_PORT: %s", err)
	}
	if B2D.DockerPort <= 0 {
		return fmt.Errorf("invalid DOCKER_PORT: must be in the range of 1--65535; got %d", B2D.DockerPort)
	}

	// TODO maybe allow flags to override ENV vars?
	flag.Parse()
	if vm := flag.Arg(1); vm != "" {
		B2D.VM = vm
	}
	return
}

func run() int {
	if err := config(); err != nil {
		logf("%s", err)
		return 1
	}

	if _, err := exec.LookPath(B2D.VBM); err != nil {
		logf("failed to locate VirtualBox management utility %q", B2D.VBM)
		return 2
	}

	switch cmd := flag.Arg(0); cmd {
	case "download":
		return cmdDownload()
	case "init":
		return cmdInit()
	case "start", "up", "boot", "resume":
		return cmdStart()
	case "ssh":
		return cmdSSH()
	case "save", "suspend":
		return cmdSave()
	case "pause":
		return cmdPause()
	case "halt", "down", "stop":
		return cmdStop()
	case "poweroff":
		return cmdPoweroff()
	case "restart":
		return cmdRestart()
	case "reset":
		return cmdReset()
	case "info":
		return cmdInfo()
	case "status":
		return cmdStatus()
	case "delete":
		return cmdDelete()
	case "version":
		fmt.Println("Client version:", Version)
		fmt.Println("Git commit:", GitSHA)
		return 0
	case "help":
		logf(usageLong)
		return 0
	case "":
		logf(usageShort)
		return 0
	default:
		logf("Unknown command '%s'", cmd)
		logf(usageShort)
		return 1
	}
}

func main() {
	os.Exit(run())
}
<|endoftext|>


<commit_before>// This is the boot2docker management utilty.
package main

import (
	"flag"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
)

// B2D reprents boot2docker config.
var B2D struct {
	VBM        string // VirtualBox management utility
	SSH        string // SSH client executable
	VM         string // virtual machine name
	Dir        string // boot2docker directory
	ISO        string // boot2docker ISO image path
	Disk       string // VM disk image path
	DiskSize   int    // VM disk image size (MB)
	Memory     int    // VM memory size (MB)
	SSHPort    int    // host SSH port (forward to port 22 in VM)
	DockerPort int    // host Docker port (forward to port 4243 in VM)
}

var usageShort = fmt.Sprintf(`Usage: %s {help|init|start|up|ssh|save|pause|stop|poweroff|reset|restart|status|info|delete|download} [vm]
`, os.Args[0])

var usageLong = fmt.Sprintf(`Usage: %s COMMAND [vm]

boot2docker management utility.

Commands:

    init            Create a new boot2docker VM.
    up|start|boot   Start the VM from any state.
    ssh             Login to VM.
    save|suspend    Suspend the VM (saving running state to disk).
    down|stop|halt  Gracefully shutdown the VM.
    restart         Gracefully reboot the VM.
    poweroff        Forcefully shutdown the VM (might cause disk corruption).
    reset           Forcefully reboot the VM (might cause disk corruption).
    delete          Delete the boot2docker VM and its disk image.
    download        Download the boot2docker ISO image.
    info            Display the detailed information of the VM
    status          Display the current state of the VM.

`, os.Args[0])

func getCfgDir(name string) (string, error) {
	if b2dDir := os.Getenv("BOOT2DOCKER_DIR"); b2dDir != "" {
		return b2dDir, nil
	}

	// Unix
	if home := os.Getenv("HOME"); home != "" {
		return filepath.Join(home, name), nil
	}

	// Windows
	for _, env := range []string{
		"APPDATA",
		"LOCALAPPDATA",
		"USERPROFILE", // let's try USERPROFILE only as a very last resort
	} {
		if val := os.Getenv(env); val != "" {
			return filepath.Join(val, "boot2docker"), nil
		}
	}
	// ok, we've tried everything reasonable - now let's go for CWD
	cwd, err := os.Getwd()
	if err != nil {
		return "", err
	}
	return filepath.Join(cwd, name), nil
}

// Read configuration.
func config() (err error) {
	B2D.VBM = getenv("BOOT2DOCKER_VBM", "VBoxManage")
	B2D.SSH = getenv("BOOT2DOCKER_SSH", "ssh")
	B2D.VM = getenv("BOOT2DOCKER_VM", "boot2docker-vm")
	if B2D.Dir, err = getCfgDir(".boot2docker"); err != nil {
		return fmt.Errorf("failed to get current directory: %s", err)
	}

	B2D.ISO = getenv("BOOT2DOCKER_ISO", filepath.Join(B2D.Dir, "boot2docker.iso"))
	B2D.Disk = getenv("BOOT2DOCKER_DISK", filepath.Join(B2D.Dir, "boot2docker.vmdk"))

	if B2D.DiskSize, err = strconv.Atoi(getenv("BOOT2DOCKER_DISKSIZE", "20000")); err != nil {
		return fmt.Errorf("invalid BOOT2DOCKER_DISKSIZE: %s", err)
	}
	if B2D.DiskSize <= 0 {
		return fmt.Errorf("BOOT2DOCKER_DISKSIZE way too small")
	}
	if B2D.Memory, err = strconv.Atoi(getenv("BOOT2DOCKER_MEMORY", "1024")); err != nil {
		return fmt.Errorf("invalid BOOT2DOCKER_MEMORY: %s", err)
	}
	if B2D.Memory <= 0 {
		return fmt.Errorf("BOOT2DOCKER_MEMORY way too small")
	}
	if B2D.SSHPort, err = strconv.Atoi(getenv("BOOT2DOCKER_SSH_PORT", "2022")); err != nil {
		return fmt.Errorf("invalid BOOT2DOCKER_SSH_PORT: %s", err)
	}
	if B2D.SSHPort <= 0 {
		return fmt.Errorf("invalid BOOT2DOCKER_SSH_PORT: must be in the range of 1--65535; got %d", B2D.SSHPort)
	}
	if B2D.DockerPort, err = strconv.Atoi(getenv("BOOT2DOCKER_DOCKER_PORT", "4243")); err != nil {
		return fmt.Errorf("invalid BOOT2DOCKER_DOCKER_PORT: %s", err)
	}
	if B2D.DockerPort <= 0 {
		return fmt.Errorf("invalid BOOT2DOCKER_DOCKER_PORT: must be in the range of 1--65535; got %d", B2D.DockerPort)
	}

	// TODO maybe allow flags to override ENV vars?
	flag.Parse()
	if vm := flag.Arg(1); vm != "" {
		B2D.VM = vm
	}
	return
}

func run() int {
	if err := config(); err != nil {
		logf("%s", err)
		return 1
	}

	if _, err := exec.LookPath(B2D.VBM); err != nil {
		logf("failed to locate VirtualBox management utility %q", B2D.VBM)
		return 2
	}

	switch flag.Arg(0) {
	case "download":
		return cmdDownload()
	case "init":
		return cmdInit()
	case "start", "up", "boot", "resume":
		return cmdStart()
	case "ssh":
		return cmdSSH()
	case "save", "suspend":
		return cmdSave()
	case "pause":
		return cmdPause()
	case "halt", "down", "stop":
		return cmdStop()
	case "poweroff":
		return cmdPoweroff()
	case "restart":
		return cmdRestart()
	case "reset":
		return cmdReset()
	case "info":
		return cmdInfo()
	case "status":
		return cmdStatus()
	case "help":
		logf(usageLong)
		return 0
	case "delete":
		return cmdDelete()
	case "":
		logf(usageShort)
		return 0
	default:
		logf("Unknown command '%s'", flag.Arg(0))
		logf(usageShort)
		return 1
	}
	return 0
}

func main() {
	os.Exit(run())
}
<commit_msg>More git-style command line help syntax<commit_after>// This is the boot2docker management utilty.
package main

import (
	"flag"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
)

// B2D reprents boot2docker config.
var B2D struct {
	VBM        string // VirtualBox management utility
	SSH        string // SSH client executable
	VM         string // virtual machine name
	Dir        string // boot2docker directory
	ISO        string // boot2docker ISO image path
	Disk       string // VM disk image path
	DiskSize   int    // VM disk image size (MB)
	Memory     int    // VM memory size (MB)
	SSHPort    int    // host SSH port (forward to port 22 in VM)
	DockerPort int    // host Docker port (forward to port 4243 in VM)
}

var usageShort = fmt.Sprintf(`Usage: %s {help|init|start|up|ssh|save|pause|stop|poweroff|reset|restart|status|info|delete|download} [<vm>]
`, os.Args[0])

var usageLong = fmt.Sprintf(`Usage: %s <command> [<vm>]

boot2docker management utility.

Commands:

    init            Create a new boot2docker VM.
    up|start|boot   Start the VM from any state.
    ssh             Login to VM.
    save|suspend    Suspend the VM (saving running state to disk).
    down|stop|halt  Gracefully shutdown the VM.
    restart         Gracefully reboot the VM.
    poweroff        Forcefully shutdown the VM (might cause disk corruption).
    reset           Forcefully reboot the VM (might cause disk corruption).
    delete          Delete the boot2docker VM and its disk image.
    download        Download the boot2docker ISO image.
    info            Display the detailed information of the VM
    status          Display the current state of the VM.

`, os.Args[0])

func getCfgDir(name string) (string, error) {
	if b2dDir := os.Getenv("BOOT2DOCKER_DIR"); b2dDir != "" {
		return b2dDir, nil
	}

	// Unix
	if home := os.Getenv("HOME"); home != "" {
		return filepath.Join(home, name), nil
	}

	// Windows
	for _, env := range []string{
		"APPDATA",
		"LOCALAPPDATA",
		"USERPROFILE", // let's try USERPROFILE only as a very last resort
	} {
		if val := os.Getenv(env); val != "" {
			return filepath.Join(val, "boot2docker"), nil
		}
	}
	// ok, we've tried everything reasonable - now let's go for CWD
	cwd, err := os.Getwd()
	if err != nil {
		return "", err
	}
	return filepath.Join(cwd, name), nil
}

// Read configuration.
func config() (err error) {
	B2D.VBM = getenv("BOOT2DOCKER_VBM", "VBoxManage")
	B2D.SSH = getenv("BOOT2DOCKER_SSH", "ssh")
	B2D.VM = getenv("BOOT2DOCKER_VM", "boot2docker-vm")
	if B2D.Dir, err = getCfgDir(".boot2docker"); err != nil {
		return fmt.Errorf("failed to get current directory: %s", err)
	}

	B2D.ISO = getenv("BOOT2DOCKER_ISO", filepath.Join(B2D.Dir, "boot2docker.iso"))
	B2D.Disk = getenv("BOOT2DOCKER_DISK", filepath.Join(B2D.Dir, "boot2docker.vmdk"))

	if B2D.DiskSize, err = strconv.Atoi(getenv("BOOT2DOCKER_DISKSIZE", "20000")); err != nil {
		return fmt.Errorf("invalid BOOT2DOCKER_DISKSIZE: %s", err)
	}
	if B2D.DiskSize <= 0 {
		return fmt.Errorf("BOOT2DOCKER_DISKSIZE way too small")
	}
	if B2D.Memory, err = strconv.Atoi(getenv("BOOT2DOCKER_MEMORY", "1024")); err != nil {
		return fmt.Errorf("invalid BOOT2DOCKER_MEMORY: %s", err)
	}
	if B2D.Memory <= 0 {
		return fmt.Errorf("BOOT2DOCKER_MEMORY way too small")
	}
	if B2D.SSHPort, err = strconv.Atoi(getenv("BOOT2DOCKER_SSH_PORT", "2022")); err != nil {
		return fmt.Errorf("invalid BOOT2DOCKER_SSH_PORT: %s", err)
	}
	if B2D.SSHPort <= 0 {
		return fmt.Errorf("invalid BOOT2DOCKER_SSH_PORT: must be in the range of 1--65535; got %d", B2D.SSHPort)
	}
	if B2D.DockerPort, err = strconv.Atoi(getenv("BOOT2DOCKER_DOCKER_PORT", "4243")); err != nil {
		return fmt.Errorf("invalid BOOT2DOCKER_DOCKER_PORT: %s", err)
	}
	if B2D.DockerPort <= 0 {
		return fmt.Errorf("invalid BOOT2DOCKER_DOCKER_PORT: must be in the range of 1--65535; got %d", B2D.DockerPort)
	}

	// TODO maybe allow flags to override ENV vars?
	flag.Parse()
	if vm := flag.Arg(1); vm != "" {
		B2D.VM = vm
	}
	return
}

func run() int {
	if err := config(); err != nil {
		logf("%s", err)
		return 1
	}

	if _, err := exec.LookPath(B2D.VBM); err != nil {
		logf("failed to locate VirtualBox management utility %q", B2D.VBM)
		return 2
	}

	switch flag.Arg(0) {
	case "download":
		return cmdDownload()
	case "init":
		return cmdInit()
	case "start", "up", "boot", "resume":
		return cmdStart()
	case "ssh":
		return cmdSSH()
	case "save", "suspend":
		return cmdSave()
	case "pause":
		return cmdPause()
	case "halt", "down", "stop":
		return cmdStop()
	case "poweroff":
		return cmdPoweroff()
	case "restart":
		return cmdRestart()
	case "reset":
		return cmdReset()
	case "info":
		return cmdInfo()
	case "status":
		return cmdStatus()
	case "help":
		logf(usageLong)
		return 0
	case "delete":
		return cmdDelete()
	case "":
		logf(usageShort)
		return 0
	default:
		logf("Unknown command '%s'", flag.Arg(0))
		logf(usageShort)
		return 1
	}
	return 0
}

func main() {
	os.Exit(run())
}
<|endoftext|>