<commit_before>package scard

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

var (
	modwinscard = syscall.NewLazyDLL("winscard.dll")

	procEstablishContext = modwinscard.NewProc("SCardEstablishContext")
	procRelease          = modwinscard.NewProc("SCardReleaseContext")
	procIsValid          = modwinscard.NewProc("SCardIsValidContext")
	procCancel           = modwinscard.NewProc("SCardCancel")
	procListReaders      = modwinscard.NewProc("SCardListReadersW")
	procGetStatusChange  = modwinscard.NewProc("SCardGetStatusChangeW")
	procConnect          = modwinscard.NewProc("SCardConnectW")
	procDisconnect       = modwinscard.NewProc("SCardDisconnect")
	procReconnect        = modwinscard.NewProc("SCardReconnect")
	procBeginTransaction = modwinscard.NewProc("SCardBeginTransaction")
	procEndTransaction   = modwinscard.NewProc("SCardEndTransaction")
	procStatus           = modwinscard.NewProc("SCardStatusW")
	procTransmit         = modwinscard.NewProc("SCardTransmit")
	procControl          = modwinscard.NewProc("SCardControl")
	procGetAttrib        = modwinscard.NewProc("SCardGetAttrib")
	procSetAttrib        = modwinscard.NewProc("SCardSetAttrib")

	dataT0Pci = modwinscard.NewProc("g_rgSCardT0Pci")
	dataT1Pci = modwinscard.NewProc("g_rgSCardT1Pci")
)

var scardIoReqT0 uintptr
var scardIoReqT1 uintptr

func init() {
	if err := dataT0Pci.Find(); err != nil {
		panic(err)
	}
	scardIoReqT0 = dataT0Pci.Addr()
	if err := dataT1Pci.Find(); err != nil {
		panic(err)
	}
	scardIoReqT1 = dataT1Pci.Addr()
}

type Context struct {
	ctx uintptr
}

type Card struct {
	handle         uintptr
	activeProtocol uintptr
}

type scardError uintptr

func (e scardError) Error() string {
	err := syscall.Errno(e)
	return fmt.Sprintf("scard: error code %x: %s", uintptr(e), err.Error())
}

// wraps SCardEstablishContext
func EstablishContext() (*Context, error) {
	var ctx Context

	r, _, _ := procEstablishContext.Call(2, uintptr(0), uintptr(0), uintptr(unsafe.Pointer(&ctx.ctx)))
	if scardError(r) != S_SUCCESS {
		return nil, scardError(r)
	}

	return &ctx, nil
}

// wraps SCardIsValidContext
func (ctx *Context) IsValid() (bool, error) {
	r, _, _ := procIsValid.Call(ctx.ctx)
	if scardError(r) != S_SUCCESS {
		return false, scardError(r)
	}
	return true, nil
}

// wraps SCardCancel
func (ctx *Context) Cancel() error {
	r, _, _ := procCancel.Call(ctx.ctx)
	if scardError(r) != S_SUCCESS {
		return scardError(r)
	}
	return nil
}

// wraps SCardReleaseContext
func (ctx *Context) Release() error {
	r, _, _ := procRelease.Call(uintptr(ctx.ctx))
	if scardError(r) != S_SUCCESS {
		return scardError(r)
	}
	return nil
}

// wraps SCardListReaders
func (ctx *Context) ListReaders() ([]string, error) {
	var needed uintptr

	r, _, _ := procListReaders.Call(
		ctx.ctx,
		0,
		0,
		uintptr(unsafe.Pointer(&needed)))
	if scardError(r) != S_SUCCESS {
		return nil, scardError(r)
	}

	data := make([]uint16, needed)
	needed <<= 1
	r, _, _ = procListReaders.Call(
		ctx.ctx,
		0,
		uintptr(unsafe.Pointer(&data[0])),
		uintptr(unsafe.Pointer(&needed)))
	if scardError(r) != S_SUCCESS {
		return nil, scardError(r)
	}

	var readers []string
	for len(data) > 0 {
		pos := 0
		for ; pos < len(data); pos++ {
			if data[pos] == 0 {
				break
			}
		}
		reader := syscall.UTF16ToString(data[:pos])
		readers = append(readers, reader)
		data = data[pos+1:]
	}

	return readers, nil
}

// wraps SCardListReaderGroups
func (ctx *Context) ListReaderGroups() ([]string, error) {
	panic("scard: not implemented")
}

type _SCARD_READERSTATE struct {
	szReader       uintptr
	pvUserData     uintptr
	dwCurrentState uint32
	dwEventState   uint32
	cbAtr          uint32
	rgbAtr         [36]byte
}

// wraps SCardGetStatusChange
func (ctx *Context) GetStatusChange(readerStates []ReaderState, timeout Timeout) error {
	crs := make([]_SCARD_READERSTATE, len(readerStates))

	for i := range readerStates {
		rptr, err := syscall.UTF16PtrFromString(readerStates[i].Reader)
		if err != nil {
			return err
		}
		crs[i].szReader = uintptr(unsafe.Pointer(rptr))
		crs[i].dwCurrentState = uint32(readerStates[i].CurrentState)
	}

	r, _, _ := procGetStatusChange.Call(
		ctx.ctx,
		uintptr(timeout),
		uintptr(unsafe.Pointer(&crs[0])),
		uintptr(len(crs)))

	if scardError(r) != S_SUCCESS {
		return scardError(r)
	}

	for i := range readerStates {
		readerStates[i].EventState = StateFlag(crs[i].dwEventState)
	}

	return nil
}

// wraps SCardConnect
func (ctx *Context) Connect(reader string, mode ShareMode, proto Protocol) (*Card, error) {
	var card Card

	creader, err := syscall.UTF16PtrFromString(reader)
	if err != nil {
		return nil, err
	}

	r, _, _ := procConnect.Call(
		ctx.ctx,
		uintptr(unsafe.Pointer(creader)),
		uintptr(mode),
		uintptr(proto),
		uintptr(unsafe.Pointer(&card.handle)),
		uintptr(unsafe.Pointer(&card.activeProtocol)))

	if scardError(r) != S_SUCCESS {
		return nil, scardError(r)
	}

	return &card, nil
}

// wraps SCardDisconnect
func (card *Card) Disconnect(d Disposition) error {
	r, _, _ := procDisconnect.Call(card.handle, uintptr(d))
	if scardError(r) != S_SUCCESS {
		return scardError(r)
	}
	return nil
}

// wraps SCardReconnect
func (card *Card) Reconnect(mode ShareMode, protocol Protocol, init Disposition) error {
	r, _, _ := procReconnect.Call(card.handle, uintptr(protocol), uintptr(init))
	if scardError(r) != S_SUCCESS {
		return scardError(r)
	}
	return nil
}

// wraps SCardBeginTransaction
func (card *Card) BeginTransaction() error {
	r, _, _ := procBeginTransaction.Call(card.handle)
	if scardError(r) != S_SUCCESS {
		return scardError(r)
	}
	return nil
}

// wraps SCardEndTransaction
func (card *Card) EndTransaction(d Disposition) error {
	r, _, _ := procEndTransaction.Call(card.handle, uintptr(d))
	if scardError(r) != S_SUCCESS {
		return scardError(r)
	}
	return nil
}

// wraps SCardStatus
func (card *Card) Status() (*CardStatus, error) {
	var reader [MAX_READERNAME + 1]uint16
	var readerLen = uint32(len(reader))
	var state, proto uint32
	var atr [MAX_ATR_SIZE]byte
	var atrLen = uint32(len(atr))

	r, _, _ := procStatus.Call(
		card.handle,
		uintptr(unsafe.Pointer(&reader[0])),
		uintptr(unsafe.Pointer(&readerLen)),
		uintptr(unsafe.Pointer(&state)),
		uintptr(unsafe.Pointer(&proto)),
		uintptr(unsafe.Pointer(&atr[0])),
		uintptr(unsafe.Pointer(&atrLen)))
	if scardError(r) != S_SUCCESS {
		return nil, scardError(r)
	}

	status := &CardStatus{
		Reader:         syscall.UTF16ToString(reader[0:readerLen]),
		State:          State(state),
		ActiveProtocol: Protocol(proto),
		ATR:            atr[0:atrLen],
	}

	return status, nil
}

// wraps SCardTransmit
func (card *Card) Transmit(cmd []byte) ([]byte, error) {
	var sendpci uintptr

	switch Protocol(card.activeProtocol) {
	case PROTOCOL_T0:
		sendpci = scardIoReqT0
	case PROTOCOL_T1:
		sendpci = scardIoReqT1
	default:
		panic("unknown protocol")
	}

	var recv [MAX_BUFFER_SIZE_EXTENDED]byte
	var recvlen = uint32(len(recv))

	r, _, _ := procTransmit.Call(card.handle,
		sendpci,
		uintptr(unsafe.Pointer(&cmd[0])),
		uintptr(len(cmd)),
		0,
		uintptr(unsafe.Pointer(&recv[0])),
		uintptr(unsafe.Pointer(&recvlen)))
	if scardError(r) != S_SUCCESS {
		return nil, scardError(r)
	}

	rsp := make([]byte, recvlen)
	copy(rsp, recv[0:recvlen])

	return rsp, nil
}

// wraps SCardControl
func (card *Card) Control(ctrl uint32, cmd []byte) ([]byte, error) {
	var recv [0xffff]byte
	var recvlen uintptr
	var r uintptr

	if len(cmd) == 0 {
		r, _, _ = procControl.Call(
			card.handle,
			uintptr(ctrl),
			0,
			0,
			uintptr(unsafe.Pointer(&recv[0])),
			uintptr(len(recv)),
			uintptr(unsafe.Pointer(&recvlen)))
	} else {
		r, _, _ = procControl.Call(
			card.handle,
			uintptr(ctrl),
			uintptr(unsafe.Pointer(&cmd[0])),
			uintptr(len(cmd)),
			uintptr(unsafe.Pointer(&recv[0])),
			uintptr(len(recv)),
			uintptr(unsafe.Pointer(&recvlen)))
	}
	if scardError(r) != S_SUCCESS {
		return nil, scardError(r)
	}

	rsp := make([]byte, recvlen)
	copy(rsp, recv[0:recvlen])

	return rsp, nil
}

// wraps SCardGetAttrib
func (card *Card) GetAttrib(id uint32) ([]byte, error) {
	var needed uintptr

	r, _, _ := procGetAttrib.Call(
		card.handle,
		uintptr(id),
		0,
		uintptr(unsafe.Pointer(&needed)))
	if scardError(r) != S_SUCCESS {
		return nil, scardError(r)
	}

	var attrib = make([]byte, needed)

	r, _, _ = procGetAttrib.Call(
		card.handle,
		uintptr(id),
		uintptr(unsafe.Pointer(&attrib[0])),
		uintptr(unsafe.Pointer(&needed)))
	if scardError(r) != S_SUCCESS {
		return nil, scardError(r)
	}

	return attrib[0:needed], nil
}

// wraps SCardSetAttrib
func (card *Card) SetAttrib(id uint32, data []byte) error {
	r, _, _ := procSetAttrib.Call(
		card.handle,
		uintptr(id),
		uintptr(unsafe.Pointer(&data[0])),
		uintptr(len(data)))
	if scardError(r) != S_SUCCESS {
		return scardError(r)
	}
	return nil
}
<commit_msg>win: needed is len in characters not byte<commit_after>package scard

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

var (
	modwinscard = syscall.NewLazyDLL("winscard.dll")

	procEstablishContext = modwinscard.NewProc("SCardEstablishContext")
	procRelease          = modwinscard.NewProc("SCardReleaseContext")
	procIsValid          = modwinscard.NewProc("SCardIsValidContext")
	procCancel           = modwinscard.NewProc("SCardCancel")
	procListReaders      = modwinscard.NewProc("SCardListReadersW")
	procGetStatusChange  = modwinscard.NewProc("SCardGetStatusChangeW")
	procConnect          = modwinscard.NewProc("SCardConnectW")
	procDisconnect       = modwinscard.NewProc("SCardDisconnect")
	procReconnect        = modwinscard.NewProc("SCardReconnect")
	procBeginTransaction = modwinscard.NewProc("SCardBeginTransaction")
	procEndTransaction   = modwinscard.NewProc("SCardEndTransaction")
	procStatus           = modwinscard.NewProc("SCardStatusW")
	procTransmit         = modwinscard.NewProc("SCardTransmit")
	procControl          = modwinscard.NewProc("SCardControl")
	procGetAttrib        = modwinscard.NewProc("SCardGetAttrib")
	procSetAttrib        = modwinscard.NewProc("SCardSetAttrib")

	dataT0Pci = modwinscard.NewProc("g_rgSCardT0Pci")
	dataT1Pci = modwinscard.NewProc("g_rgSCardT1Pci")
)

var scardIoReqT0 uintptr
var scardIoReqT1 uintptr

func init() {
	if err := dataT0Pci.Find(); err != nil {
		panic(err)
	}
	scardIoReqT0 = dataT0Pci.Addr()
	if err := dataT1Pci.Find(); err != nil {
		panic(err)
	}
	scardIoReqT1 = dataT1Pci.Addr()
}

type Context struct {
	ctx uintptr
}

type Card struct {
	handle         uintptr
	activeProtocol uintptr
}

type scardError uintptr

func (e scardError) Error() string {
	err := syscall.Errno(e)
	return fmt.Sprintf("scard: error code %x: %s", uintptr(e), err.Error())
}

// wraps SCardEstablishContext
func EstablishContext() (*Context, error) {
	var ctx Context

	r, _, _ := procEstablishContext.Call(2, uintptr(0), uintptr(0), uintptr(unsafe.Pointer(&ctx.ctx)))
	if scardError(r) != S_SUCCESS {
		return nil, scardError(r)
	}

	return &ctx, nil
}

// wraps SCardIsValidContext
func (ctx *Context) IsValid() (bool, error) {
	r, _, _ := procIsValid.Call(ctx.ctx)
	if scardError(r) != S_SUCCESS {
		return false, scardError(r)
	}
	return true, nil
}

// wraps SCardCancel
func (ctx *Context) Cancel() error {
	r, _, _ := procCancel.Call(ctx.ctx)
	if scardError(r) != S_SUCCESS {
		return scardError(r)
	}
	return nil
}

// wraps SCardReleaseContext
func (ctx *Context) Release() error {
	r, _, _ := procRelease.Call(uintptr(ctx.ctx))
	if scardError(r) != S_SUCCESS {
		return scardError(r)
	}
	return nil
}

// wraps SCardListReaders
func (ctx *Context) ListReaders() ([]string, error) {
	var needed uintptr

	r, _, _ := procListReaders.Call(
		ctx.ctx,
		0,
		0,
		uintptr(unsafe.Pointer(&needed)))
	if scardError(r) != S_SUCCESS {
		return nil, scardError(r)
	}

	data := make([]uint16, needed)
	r, _, _ = procListReaders.Call(
		ctx.ctx,
		0,
		uintptr(unsafe.Pointer(&data[0])),
		uintptr(unsafe.Pointer(&needed)))
	if scardError(r) != S_SUCCESS {
		return nil, scardError(r)
	}

	var readers []string
	for len(data) > 0 {
		pos := 0
		for ; pos < len(data); pos++ {
			if data[pos] == 0 {
				break
			}
		}
		reader := syscall.UTF16ToString(data[:pos])
		readers = append(readers, reader)
		data = data[pos+1:]
	}

	return readers, nil
}

// wraps SCardListReaderGroups
func (ctx *Context) ListReaderGroups() ([]string, error) {
	panic("scard: not implemented")
}

type _SCARD_READERSTATE struct {
	szReader       uintptr
	pvUserData     uintptr
	dwCurrentState uint32
	dwEventState   uint32
	cbAtr          uint32
	rgbAtr         [36]byte
}

// wraps SCardGetStatusChange
func (ctx *Context) GetStatusChange(readerStates []ReaderState, timeout Timeout) error {
	crs := make([]_SCARD_READERSTATE, len(readerStates))

	for i := range readerStates {
		rptr, err := syscall.UTF16PtrFromString(readerStates[i].Reader)
		if err != nil {
			return err
		}
		crs[i].szReader = uintptr(unsafe.Pointer(rptr))
		crs[i].dwCurrentState = uint32(readerStates[i].CurrentState)
	}

	r, _, _ := procGetStatusChange.Call(
		ctx.ctx,
		uintptr(timeout),
		uintptr(unsafe.Pointer(&crs[0])),
		uintptr(len(crs)))

	if scardError(r) != S_SUCCESS {
		return scardError(r)
	}

	for i := range readerStates {
		readerStates[i].EventState = StateFlag(crs[i].dwEventState)
	}

	return nil
}

// wraps SCardConnect
func (ctx *Context) Connect(reader string, mode ShareMode, proto Protocol) (*Card, error) {
	var card Card

	creader, err := syscall.UTF16PtrFromString(reader)
	if err != nil {
		return nil, err
	}

	r, _, _ := procConnect.Call(
		ctx.ctx,
		uintptr(unsafe.Pointer(creader)),
		uintptr(mode),
		uintptr(proto),
		uintptr(unsafe.Pointer(&card.handle)),
		uintptr(unsafe.Pointer(&card.activeProtocol)))

	if scardError(r) != S_SUCCESS {
		return nil, scardError(r)
	}

	return &card, nil
}

// wraps SCardDisconnect
func (card *Card) Disconnect(d Disposition) error {
	r, _, _ := procDisconnect.Call(card.handle, uintptr(d))
	if scardError(r) != S_SUCCESS {
		return scardError(r)
	}
	return nil
}

// wraps SCardReconnect
func (card *Card) Reconnect(mode ShareMode, protocol Protocol, init Disposition) error {
	r, _, _ := procReconnect.Call(card.handle, uintptr(protocol), uintptr(init))
	if scardError(r) != S_SUCCESS {
		return scardError(r)
	}
	return nil
}

// wraps SCardBeginTransaction
func (card *Card) BeginTransaction() error {
	r, _, _ := procBeginTransaction.Call(card.handle)
	if scardError(r) != S_SUCCESS {
		return scardError(r)
	}
	return nil
}

// wraps SCardEndTransaction
func (card *Card) EndTransaction(d Disposition) error {
	r, _, _ := procEndTransaction.Call(card.handle, uintptr(d))
	if scardError(r) != S_SUCCESS {
		return scardError(r)
	}
	return nil
}

// wraps SCardStatus
func (card *Card) Status() (*CardStatus, error) {
	var reader [MAX_READERNAME + 1]uint16
	var readerLen = uint32(len(reader))
	var state, proto uint32
	var atr [MAX_ATR_SIZE]byte
	var atrLen = uint32(len(atr))

	r, _, _ := procStatus.Call(
		card.handle,
		uintptr(unsafe.Pointer(&reader[0])),
		uintptr(unsafe.Pointer(&readerLen)),
		uintptr(unsafe.Pointer(&state)),
		uintptr(unsafe.Pointer(&proto)),
		uintptr(unsafe.Pointer(&atr[0])),
		uintptr(unsafe.Pointer(&atrLen)))
	if scardError(r) != S_SUCCESS {
		return nil, scardError(r)
	}

	status := &CardStatus{
		Reader:         syscall.UTF16ToString(reader[0:readerLen]),
		State:          State(state),
		ActiveProtocol: Protocol(proto),
		ATR:            atr[0:atrLen],
	}

	return status, nil
}

// wraps SCardTransmit
func (card *Card) Transmit(cmd []byte) ([]byte, error) {
	var sendpci uintptr

	switch Protocol(card.activeProtocol) {
	case PROTOCOL_T0:
		sendpci = scardIoReqT0
	case PROTOCOL_T1:
		sendpci = scardIoReqT1
	default:
		panic("unknown protocol")
	}

	var recv [MAX_BUFFER_SIZE_EXTENDED]byte
	var recvlen = uint32(len(recv))

	r, _, _ := procTransmit.Call(card.handle,
		sendpci,
		uintptr(unsafe.Pointer(&cmd[0])),
		uintptr(len(cmd)),
		0,
		uintptr(unsafe.Pointer(&recv[0])),
		uintptr(unsafe.Pointer(&recvlen)))
	if scardError(r) != S_SUCCESS {
		return nil, scardError(r)
	}

	rsp := make([]byte, recvlen)
	copy(rsp, recv[0:recvlen])

	return rsp, nil
}

// wraps SCardControl
func (card *Card) Control(ctrl uint32, cmd []byte) ([]byte, error) {
	var recv [0xffff]byte
	var recvlen uintptr
	var r uintptr

	if len(cmd) == 0 {
		r, _, _ = procControl.Call(
			card.handle,
			uintptr(ctrl),
			0,
			0,
			uintptr(unsafe.Pointer(&recv[0])),
			uintptr(len(recv)),
			uintptr(unsafe.Pointer(&recvlen)))
	} else {
		r, _, _ = procControl.Call(
			card.handle,
			uintptr(ctrl),
			uintptr(unsafe.Pointer(&cmd[0])),
			uintptr(len(cmd)),
			uintptr(unsafe.Pointer(&recv[0])),
			uintptr(len(recv)),
			uintptr(unsafe.Pointer(&recvlen)))
	}
	if scardError(r) != S_SUCCESS {
		return nil, scardError(r)
	}

	rsp := make([]byte, recvlen)
	copy(rsp, recv[0:recvlen])

	return rsp, nil
}

// wraps SCardGetAttrib
func (card *Card) GetAttrib(id uint32) ([]byte, error) {
	var needed uintptr

	r, _, _ := procGetAttrib.Call(
		card.handle,
		uintptr(id),
		0,
		uintptr(unsafe.Pointer(&needed)))
	if scardError(r) != S_SUCCESS {
		return nil, scardError(r)
	}

	var attrib = make([]byte, needed)

	r, _, _ = procGetAttrib.Call(
		card.handle,
		uintptr(id),
		uintptr(unsafe.Pointer(&attrib[0])),
		uintptr(unsafe.Pointer(&needed)))
	if scardError(r) != S_SUCCESS {
		return nil, scardError(r)
	}

	return attrib[0:needed], nil
}

// wraps SCardSetAttrib
func (card *Card) SetAttrib(id uint32, data []byte) error {
	r, _, _ := procSetAttrib.Call(
		card.handle,
		uintptr(id),
		uintptr(unsafe.Pointer(&data[0])),
		uintptr(len(data)))
	if scardError(r) != S_SUCCESS {
		return scardError(r)
	}
	return nil
}
<|endoftext|>


<commit_before>/*
MIT License

Copyright (c) 2016 Timo Reimann

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

package main

import (
	"fmt"
	"log"

	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/api/errors"
	"k8s.io/kubernetes/pkg/api/resource"
	vapi "k8s.io/kubernetes/pkg/api/unversioned"
	"k8s.io/kubernetes/pkg/apis/extensions"
	client "k8s.io/kubernetes/pkg/client/unversioned"
	"k8s.io/kubernetes/pkg/util/intstr"
)

const namespace string = "default"

// operation represents a Kubernetes operation.
type operation interface {
	Do(c *client.Client)
}

type versionOperation struct{}

func (op *versionOperation) Do(c *client.Client) {
	info, err := c.Discovery().ServerVersion()
	if err != nil {
		logger.Fatalf("failed to retrieve server API version: %s\n", err)
	}

	logger.Printf("server API version information: %s\n", info)
}

type deployOperation struct {
	image string
	name  string
	port  int
}

func (op *deployOperation) Do(c *client.Client) {
	if err := op.doDeployment(c); err != nil {
		log.Fatal(err)
	}

	if err := op.doService(c); err != nil {
		log.Fatal(err)
	}
}

func (op *deployOperation) doDeployment(c *client.Client) error {
	appName := op.name

	// Define Deployments spec.
	deploySpec := &extensions.Deployment{
		TypeMeta: vapi.TypeMeta{
			Kind:       "Deployment",
			APIVersion: "extensions/v1beta1",
		},
		ObjectMeta: api.ObjectMeta{
			Name: appName,
		},
		Spec: extensions.DeploymentSpec{
			Replicas: 1,
			Template: api.PodTemplateSpec{
				ObjectMeta: api.ObjectMeta{
					Name:   appName,
					Labels: map[string]string{"app": appName},
				},
				Spec: api.PodSpec{
					Containers: []api.Container{
						api.Container{
							Name:  op.name,
							Image: op.image,
							Ports: []api.ContainerPort{
								api.ContainerPort{ContainerPort: op.port, Protocol: api.ProtocolTCP},
							},
							Resources: api.ResourceRequirements{
								Limits: api.ResourceList{
									api.ResourceCPU:    resource.MustParse("100m"),
									api.ResourceMemory: resource.MustParse("256Mi"),
								},
							},
							ImagePullPolicy: api.PullIfNotPresent,
						},
					},
					RestartPolicy: api.RestartPolicyAlways,
					DNSPolicy:     api.DNSClusterFirst,
				},
			},
		},
	}

	// Implement deployment update-or-create semantics.
	deploy := c.Extensions().Deployments(namespace)
	_, err := deploy.Update(deploySpec)
	switch {
	case err == nil:
		logger.Println("deployment controller updated")
	case !errors.IsNotFound(err):
		return fmt.Errorf("could not update deployment controller: %s", err)
	default:
		_, err = deploy.Create(deploySpec)
		if err != nil {
			return fmt.Errorf("could not create deployment controller: %s", err)
		}
		logger.Println("deployment controller created")
	}

	return nil
}

func (op *deployOperation) doService(c *client.Client) error {
	appName := op.name

	// Define service spec.
	serviceSpec := &api.Service{
		TypeMeta: vapi.TypeMeta{
			Kind:       "Service",
			APIVersion: "v1",
		},
		ObjectMeta: api.ObjectMeta{
			Name: appName,
		},
		Spec: api.ServiceSpec{
			Type:     api.ServiceTypeClusterIP,
			Selector: map[string]string{"app": appName},
			Ports: []api.ServicePort{
				api.ServicePort{
					Protocol: api.ProtocolTCP,
					Port:     80,
					TargetPort: intstr.IntOrString{
						Type:   intstr.Int,
						IntVal: int32(op.port),
					},
				},
			},
		},
	}

	// Implement service update-or-create semantics.
	service := c.Services(namespace)
	svc, err := service.Get(appName)
	switch {
	case err == nil:
		serviceSpec.ObjectMeta.ResourceVersion = svc.ObjectMeta.ResourceVersion
		serviceSpec.Spec.ClusterIP = svc.Spec.ClusterIP
		_, err = service.Update(serviceSpec)
		if err != nil {
			return fmt.Errorf("failed to update service: %s", err)
		}
		logger.Println("service updated")
	case errors.IsNotFound(err):
		_, err = service.Create(serviceSpec)
		if err != nil {
			return fmt.Errorf("failed to create service: %s", err)
		}
		logger.Println("service created")
	default:
		return fmt.Errorf("unexpected error: %s", err)
	}

	return nil
}
<commit_msg>Set deployment strategy and revision history limit.<commit_after>/*
MIT License

Copyright (c) 2016 Timo Reimann

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

package main

import (
	"fmt"
	"log"

	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/api/errors"
	"k8s.io/kubernetes/pkg/api/resource"
	vapi "k8s.io/kubernetes/pkg/api/unversioned"
	"k8s.io/kubernetes/pkg/apis/extensions"
	client "k8s.io/kubernetes/pkg/client/unversioned"
	"k8s.io/kubernetes/pkg/util/intstr"
)

const namespace string = "default"

// operation represents a Kubernetes operation.
type operation interface {
	Do(c *client.Client)
}

type versionOperation struct{}

func (op *versionOperation) Do(c *client.Client) {
	info, err := c.Discovery().ServerVersion()
	if err != nil {
		logger.Fatalf("failed to retrieve server API version: %s\n", err)
	}

	logger.Printf("server API version information: %s\n", info)
}

type deployOperation struct {
	image string
	name  string
	port  int
}

func (op *deployOperation) Do(c *client.Client) {
	if err := op.doDeployment(c); err != nil {
		log.Fatal(err)
	}

	if err := op.doService(c); err != nil {
		log.Fatal(err)
	}
}

func (op *deployOperation) doDeployment(c *client.Client) error {
	appName := op.name

	// Define Deployments spec.
	deploySpec := &extensions.Deployment{
		TypeMeta: vapi.TypeMeta{
			Kind:       "Deployment",
			APIVersion: "extensions/v1beta1",
		},
		ObjectMeta: api.ObjectMeta{
			Name: appName,
		},
		Spec: extensions.DeploymentSpec{
			Replicas: 1,
			Strategy: extensions.DeploymentStrategy{
				Type: extensions.RollingUpdateDeploymentStrategyType,
				RollingUpdate: &extensions.RollingUpdateDeployment{
					MaxUnavailable: intstr.IntOrString{
						Type:   intstr.Int,
						IntVal: int32(0),
					},
					MaxSurge: intstr.IntOrString{
						Type:   intstr.Int,
						IntVal: int32(1),
					},
				},
			},
			RevisionHistoryLimit: intp(10),
			Template: api.PodTemplateSpec{
				ObjectMeta: api.ObjectMeta{
					Name:   appName,
					Labels: map[string]string{"app": appName},
				},
				Spec: api.PodSpec{
					Containers: []api.Container{
						api.Container{
							Name:  op.name,
							Image: op.image,
							Ports: []api.ContainerPort{
								api.ContainerPort{ContainerPort: op.port, Protocol: api.ProtocolTCP},
							},
							Resources: api.ResourceRequirements{
								Limits: api.ResourceList{
									api.ResourceCPU:    resource.MustParse("100m"),
									api.ResourceMemory: resource.MustParse("256Mi"),
								},
							},
							ImagePullPolicy: api.PullIfNotPresent,
						},
					},
					RestartPolicy: api.RestartPolicyAlways,
					DNSPolicy:     api.DNSClusterFirst,
				},
			},
		},
	}

	// Implement deployment update-or-create semantics.
	deploy := c.Extensions().Deployments(namespace)
	_, err := deploy.Update(deploySpec)
	switch {
	case err == nil:
		logger.Println("deployment controller updated")
	case !errors.IsNotFound(err):
		return fmt.Errorf("could not update deployment controller: %s", err)
	default:
		_, err = deploy.Create(deploySpec)
		if err != nil {
			return fmt.Errorf("could not create deployment controller: %s", err)
		}
		logger.Println("deployment controller created")
	}

	return nil
}

func (op *deployOperation) doService(c *client.Client) error {
	appName := op.name

	// Define service spec.
	serviceSpec := &api.Service{
		TypeMeta: vapi.TypeMeta{
			Kind:       "Service",
			APIVersion: "v1",
		},
		ObjectMeta: api.ObjectMeta{
			Name: appName,
		},
		Spec: api.ServiceSpec{
			Type:     api.ServiceTypeClusterIP,
			Selector: map[string]string{"app": appName},
			Ports: []api.ServicePort{
				api.ServicePort{
					Protocol: api.ProtocolTCP,
					Port:     80,
					TargetPort: intstr.IntOrString{
						Type:   intstr.Int,
						IntVal: int32(op.port),
					},
				},
			},
		},
	}

	// Implement service update-or-create semantics.
	service := c.Services(namespace)
	svc, err := service.Get(appName)
	switch {
	case err == nil:
		serviceSpec.ObjectMeta.ResourceVersion = svc.ObjectMeta.ResourceVersion
		serviceSpec.Spec.ClusterIP = svc.Spec.ClusterIP
		_, err = service.Update(serviceSpec)
		if err != nil {
			return fmt.Errorf("failed to update service: %s", err)
		}
		logger.Println("service updated")
	case errors.IsNotFound(err):
		_, err = service.Create(serviceSpec)
		if err != nil {
			return fmt.Errorf("failed to create service: %s", err)
		}
		logger.Println("service created")
	default:
		return fmt.Errorf("unexpected error: %s", err)
	}

	return nil
}

func intp(i int) *int {
	r := new(int)
	*r = i
	return r
}
<|endoftext|>


<commit_before>package storage

import "fmt"
import "time"
import "launchpad.net/gozk/zookeeper"

/**
 * ZooKeeper Storage Manager
 *
 * @author: Anant Bhardwaj
 * @date: 05/11/2014
 */

type StorageManager struct {
  conn *zookeeper.Conn
}

func MakeStorageManager() *StorageManager {
  sm := &StorageManager{}
  return sm
}

func (sm *StorageManager) Open(servers string) error {
  conn, session, err := zookeeper.Dial(servers, 5 * time.Second)
  if err != nil {
    fmt.Printf("Can't connect to zookeeper: %v\n", err)
    return err
  }

  // Wait for connection.
  event := <-session
  if event.State != zookeeper.STATE_CONNECTED {
    fmt.Printf("Can't connect to zookeeper: %v\n", event)
    return err
  }

  sm.conn = conn
  return nil
}

func (sm *StorageManager) Close() error {
  return sm.conn.Close()
}

func (sm *StorageManager) Create(path string, data string) error {
  stats, _ := sm.conn.Exists(path)
  var err error

  if stats == nil {
    _, err = sm.conn.Create(path, data, 0, zookeeper.WorldACL(zookeeper.PERM_ALL))
  }

  if err != nil {
    fmt.Printf("Error creating a node (%v): %v\n", path, err)
  } else {
    fmt.Printf("Created node (%v): %v\n", path, data)
  }

  return err
}

func (sm *StorageManager) Delete(path string) error {

  err := sm.conn.Delete(path, -1)

  if err != nil {
    fmt.Printf("Error writing to node (%v): %v\n", path, err)
  } else {
    fmt.Printf("Deleted node (%v)", path)
  }

  return err
}

func (sm *StorageManager) Write(path string, data string) error {

  _, err := sm.conn.Set(path, data, -1)

  if err != nil {
    err_create := sm.Create(path, data)
    if err_create != nil {
      fmt.Printf("Error writing to node (%v): %v\n", path, err)
      return err_create
    }
  }

  fmt.Printf("Written node (%v): %v\n", path, data)
  return err
}

func (sm *StorageManager) Read(path string) (string, error) {
  data, _, err := sm.conn.Get(path)

  if err != nil {
    fmt.Printf("Error reading from node (%v): %v\n", path, err)
  }

  fmt.Printf("Read node (%v): %v\n", path, data)

  return data, err
}
<commit_msg>persistant paxos<commit_after>package storage

import "fmt"
import "time"
import "launchpad.net/gozk/zookeeper"

/**
 * ZooKeeper Storage Manager
 *
 * @author: Anant Bhardwaj
 * @date: 05/11/2014
 */

type StorageManager struct {
  conn *zookeeper.Conn
}

func MakeStorageManager() *StorageManager {
  sm := &StorageManager{}
  return sm
}

func (sm *StorageManager) Open(servers string) error {
  conn, session, err := zookeeper.Dial(servers, 5 * time.Second)
  if err != nil {
    fmt.Printf("Can't connect to zookeeper: %v\n", err)
    return err
  }

  // Wait for connection.
  event := <-session
  if event.State != zookeeper.STATE_CONNECTED {
    fmt.Printf("Can't connect to zookeeper: %v\n", event)
    return err
  }

  sm.conn = conn
  return nil
}

func (sm *StorageManager) Close() error {
  return sm.conn.Close()
}

func (sm *StorageManager) Create(path string, data string) error {
  stats, _ := sm.conn.Exists(path)
  var err error

  if stats == nil {
    _, err = sm.conn.Create(path, data, 0, zookeeper.WorldACL(zookeeper.PERM_ALL))
  }

  if err != nil {
    fmt.Printf("Error creating a node (%v): %v\n", path, err)
  } else {
    //fmt.Printf("Created node (%v): %v\n", path, data)
  }

  return err
}

func (sm *StorageManager) Delete(path string) error {

  err := sm.conn.Delete(path, -1)

  if err != nil {
    fmt.Printf("Error writing to node (%v): %v\n", path, err)
  } else {
    //fmt.Printf("Deleted node (%v)", path)
  }

  return err
}

func (sm *StorageManager) Write(path string, data string) error {

  _, err := sm.conn.Set(path, data, -1)

  if err != nil {
    err_create := sm.Create(path, data)
    if err_create != nil {
      fmt.Printf("Error writing to node (%v): %v\n", path, err)
      return err_create
    }
  }

  //fmt.Printf("Written node (%v): %v\n", path, data)
  return err
}

func (sm *StorageManager) Read(path string) (string, error) {
  data, _, err := sm.conn.Get(path)

  if err != nil {
    fmt.Printf("Error reading from node (%v): %v\n", path, err)
  }

  //fmt.Printf("Read node (%v): %v\n", path, data)

  return data, err
}
<|endoftext|>


<commit_before>// This file configures the screen, basically remembering the size
// and foreground and background colours.
package screen

import (
	"fmt"
	"log"
	"os"

	"github.com/nsf/termbox-go"

	"github.com/sjmudd/pstop/lib"
	"github.com/sjmudd/pstop/version"
)

// this just allows me to use stuff with it
type TermboxScreen struct {
	width, height int
	fg, bg        termbox.Attribute
}

// print the characters in bold (for headings) but don't print them outside the screen
func (s *TermboxScreen) BoldPrintAt(x int, y int, text string) {
	offset := 0
	for c := range text {
		if (x + offset) < s.width {
			termbox.SetCell(x+offset, y, rune(text[c]), s.fg|termbox.AttrBold, s.bg)
			offset++
		}
	}
	s.Flush()
}

// clear the screen
func (s *TermboxScreen) Clear() {
	termbox.Clear(termbox.ColorWhite, termbox.ColorBlack)
}

// close the screen
func (s *TermboxScreen) Close() {
	termbox.Close()
}

// display a help page
func (s *TermboxScreen) DisplayHelp() {
	s.PrintAt(0, 0, lib.MyName()+" version "+version.Version()+" "+lib.Copyright())

	s.PrintAt(0, 2, "Program to show the top I/O information by accessing information from the")
	s.PrintAt(0, 3, "performance_schema schema. Ideas based on mysql-sys.")

	s.PrintAt(0, 5, "Keys:")
	s.PrintAt(0, 6, "- - reduce the poll interval by 1 second (minimum 1 second)")
	s.PrintAt(0, 7, "+ - increase the poll interval by 1 second")
	s.PrintAt(0, 8, "h/? - this help screen")
	s.PrintAt(0, 9, "q - quit")
	s.PrintAt(0, 10, "t - toggle between showing time since resetting statistics or since P_S data was collected")
	s.PrintAt(0, 11, "z - reset statistics")
	s.PrintAt(0, 12, "<tab> or <right arrow> - change display modes between: latency, ops, file I/O, lock and user modes")
	s.PrintAt(0, 13, "<left arrow> - change display modes to the previous screen (see above)")
	s.PrintAt(0, 15, "Press h to return to main screen")
}

// flush changes to screen
func (s *TermboxScreen) Flush() {
	termbox.Flush()
}

// return the current height of the screen
func (s *TermboxScreen) Height() int {
	return s.height
}

// reset the termbox to a clear screen
func (s *TermboxScreen) Initialise() {
	err := termbox.Init()
	if err != nil {
		fmt.Println("Could not start termbox for " + lib.MyName() + ". View ~/." + lib.MyName() + ".log for error messages.")
		log.Printf("Cannot start "+lib.MyName()+", termbox.Init() gave an error:\n%s\n", err)
		os.Exit(1)
	}

	s.Clear()
	s.fg = termbox.ColorDefault
	s.bg = termbox.ColorDefault

	s.SetSize(termbox.Size())
}

// print the characters but don't print them outside the screen
func (s *TermboxScreen) PrintAt(x int, y int, text string) {
	offset := 0
	for c := range text {
		if (x + offset) < s.width {
			termbox.SetCell(x+offset, y, rune(text[c]), s.fg, s.bg)
			offset++
		}
	}
	s.Flush()
}

// set the screen size
func (s *TermboxScreen) SetSize(width, height int) {
	// if we get bigger then clear out the bottom line
	for x := 0; x < s.width; x++ {
		termbox.SetCell(x, s.height-1, ' ', s.fg, s.bg)
	}
	s.Flush()

	s.width = width
	s.height = height
}

// return the current (width, height) of the screen
func (s *TermboxScreen) Size() (int, int) {
	return s.width, s.height
}

// create a channel for termbox.Events and run a poller to send
// these events to the channel.  Return the channel.
func (s TermboxScreen) TermBoxChan() chan termbox.Event {
	termboxChan := make(chan termbox.Event)
	go func() {
		for {
			termboxChan <- termbox.PollEvent()
		}
	}()
	return termboxChan
}
<commit_msg>Clear with the default background, not black.<commit_after>// This file configures the screen, basically remembering the size
// and foreground and background colours.
package screen

import (
	"fmt"
	"log"
	"os"

	"github.com/nsf/termbox-go"

	"github.com/sjmudd/pstop/lib"
	"github.com/sjmudd/pstop/version"
)

// this just allows me to use stuff with it
type TermboxScreen struct {
	width, height int
	fg, bg        termbox.Attribute
}

// print the characters in bold (for headings) but don't print them outside the screen
func (s *TermboxScreen) BoldPrintAt(x int, y int, text string) {
	offset := 0
	for c := range text {
		if (x + offset) < s.width {
			termbox.SetCell(x+offset, y, rune(text[c]), s.fg|termbox.AttrBold, s.bg)
			offset++
		}
	}
	s.Flush()
}

// clear the screen
func (s *TermboxScreen) Clear() {
	termbox.Clear(termbox.ColorDefault, termbox.ColorDefault)
}

// close the screen
func (s *TermboxScreen) Close() {
	termbox.Close()
}

// display a help page
func (s *TermboxScreen) DisplayHelp() {
	s.PrintAt(0, 0, lib.MyName()+" version "+version.Version()+" "+lib.Copyright())

	s.PrintAt(0, 2, "Program to show the top I/O information by accessing information from the")
	s.PrintAt(0, 3, "performance_schema schema. Ideas based on mysql-sys.")

	s.PrintAt(0, 5, "Keys:")
	s.PrintAt(0, 6, "- - reduce the poll interval by 1 second (minimum 1 second)")
	s.PrintAt(0, 7, "+ - increase the poll interval by 1 second")
	s.PrintAt(0, 8, "h/? - this help screen")
	s.PrintAt(0, 9, "q - quit")
	s.PrintAt(0, 10, "t - toggle between showing time since resetting statistics or since P_S data was collected")
	s.PrintAt(0, 11, "z - reset statistics")
	s.PrintAt(0, 12, "<tab> or <right arrow> - change display modes between: latency, ops, file I/O, lock and user modes")
	s.PrintAt(0, 13, "<left arrow> - change display modes to the previous screen (see above)")
	s.PrintAt(0, 15, "Press h to return to main screen")
}

// flush changes to screen
func (s *TermboxScreen) Flush() {
	termbox.Flush()
}

// return the current height of the screen
func (s *TermboxScreen) Height() int {
	return s.height
}

// reset the termbox to a clear screen
func (s *TermboxScreen) Initialise() {
	err := termbox.Init()
	if err != nil {
		fmt.Println("Could not start termbox for " + lib.MyName() + ". View ~/." + lib.MyName() + ".log for error messages.")
		log.Printf("Cannot start "+lib.MyName()+", termbox.Init() gave an error:\n%s\n", err)
		os.Exit(1)
	}

	s.Clear()
	s.fg = termbox.ColorDefault
	s.bg = termbox.ColorDefault

	s.SetSize(termbox.Size())
}

// print the characters but don't print them outside the screen
func (s *TermboxScreen) PrintAt(x int, y int, text string) {
	offset := 0
	for c := range text {
		if (x + offset) < s.width {
			termbox.SetCell(x+offset, y, rune(text[c]), s.fg, s.bg)
			offset++
		}
	}
	s.Flush()
}

// set the screen size
func (s *TermboxScreen) SetSize(width, height int) {
	// if we get bigger then clear out the bottom line
	for x := 0; x < s.width; x++ {
		termbox.SetCell(x, s.height-1, ' ', s.fg, s.bg)
	}
	s.Flush()

	s.width = width
	s.height = height
}

// return the current (width, height) of the screen
func (s *TermboxScreen) Size() (int, int) {
	return s.width, s.height
}

// create a channel for termbox.Events and run a poller to send
// these events to the channel.  Return the channel.
func (s TermboxScreen) TermBoxChan() chan termbox.Event {
	termboxChan := make(chan termbox.Event)
	go func() {
		for {
			termboxChan <- termbox.PollEvent()
		}
	}()
	return termboxChan
}
<|endoftext|>


<commit_before>package consumer

import (
	"errors"
	"time"

	"h12.me/kafka/common"
	"h12.me/kafka/proto"
)

var (
	ErrOffsetNotFound          = errors.New("offset not found")
	ErrFailToFetchOffsetByTime = errors.New("fail to fetch offset by time")
)

type Message struct {
	Key    []byte
	Value  []byte
	Offset int64
}

type C struct {
	MaxWaitTime     time.Duration
	MinBytes        int
	MaxBytes        int
	OffsetRetention time.Duration
	cluster         common.Cluster
}

func New(cluster common.Cluster) *C {
	return &C{
		cluster:         cluster,
		MaxWaitTime:     100 * time.Millisecond,
		MinBytes:        1,
		MaxBytes:        1024 * 1024,
		OffsetRetention: 7 * 24 * time.Hour,
	}
}

func (c *C) SearchOffsetByTime(topic string, partition int32, keyTime time.Time, getTime proto.GetTimeFunc) (int64, error) {
	return (&proto.OffsetByTime{
		Topic:     topic,
		Partition: partition,
		Time:      keyTime,
	}).Search(c.cluster, getTime)
}

func (c *C) Offset(topic string, partition int32, consumerGroup string) (int64, error) {
	return (&proto.Offset{Topic: topic, Partition: partition, Group: consumerGroup}).Fetch(c.cluster)
}

func (c *C) Consume(topic string, partition int32, offset int64) (messages []Message, err error) {
	ms, err := (&proto.Messages{
		Topic:       topic,
		Partition:   partition,
		Offset:      offset,
		MinBytes:    c.MinBytes,
		MaxBytes:    c.MaxBytes,
		MaxWaitTime: c.MaxWaitTime,
	}).Consume(c.cluster)
	if err != nil {
		return nil, err
	}
	for i := range ms {
		m := &ms[i].SizedMessage.CRCMessage.Message
		messages = append(messages, Message{
			Key:    m.Key,
			Value:  m.Value,
			Offset: ms[i].Offset,
		})
	}
	return
}

func (c *C) Commit(topic string, partition int32, consumerGroup string, offset int64) error {
	return (&proto.Offset{
		Topic:     topic,
		Partition: partition,
		Group:     consumerGroup,
		Offset:    offset,
	}).Commit(c.cluster)
}
<commit_msg>add FetchOffsetByTime<commit_after>package consumer

import (
	"errors"
	"time"

	"h12.me/kafka/common"
	"h12.me/kafka/proto"
)

var (
	ErrOffsetNotFound          = errors.New("offset not found")
	ErrFailToFetchOffsetByTime = errors.New("fail to fetch offset by time")
)

type Message struct {
	Key    []byte
	Value  []byte
	Offset int64
}

type C struct {
	MaxWaitTime     time.Duration
	MinBytes        int
	MaxBytes        int
	OffsetRetention time.Duration
	cluster         common.Cluster
}

func New(cluster common.Cluster) *C {
	return &C{
		cluster:         cluster,
		MaxWaitTime:     100 * time.Millisecond,
		MinBytes:        1,
		MaxBytes:        1024 * 1024,
		OffsetRetention: 7 * 24 * time.Hour,
	}
}

func (c *C) FetchOffsetByTime(topic string, partition int32, keyTime time.Time) (int64, error) {
	return (&proto.OffsetByTime{
		Topic:     topic,
		Partition: partition,
		Time:      keyTime,
	}).Fetch(c.cluster)
}

func (c *C) SearchOffsetByTime(topic string, partition int32, keyTime time.Time, getTime proto.GetTimeFunc) (int64, error) {
	return (&proto.OffsetByTime{
		Topic:     topic,
		Partition: partition,
		Time:      keyTime,
	}).Search(c.cluster, getTime)
}

func (c *C) Offset(topic string, partition int32, consumerGroup string) (int64, error) {
	return (&proto.Offset{Topic: topic, Partition: partition, Group: consumerGroup}).Fetch(c.cluster)
}

func (c *C) Consume(topic string, partition int32, offset int64) (messages []Message, err error) {
	ms, err := (&proto.Messages{
		Topic:       topic,
		Partition:   partition,
		Offset:      offset,
		MinBytes:    c.MinBytes,
		MaxBytes:    c.MaxBytes,
		MaxWaitTime: c.MaxWaitTime,
	}).Consume(c.cluster)
	if err != nil {
		return nil, err
	}
	for i := range ms {
		m := &ms[i].SizedMessage.CRCMessage.Message
		messages = append(messages, Message{
			Key:    m.Key,
			Value:  m.Value,
			Offset: ms[i].Offset,
		})
	}
	return
}

func (c *C) Commit(topic string, partition int32, consumerGroup string, offset int64) error {
	return (&proto.Offset{
		Topic:     topic,
		Partition: partition,
		Group:     consumerGroup,
		Offset:    offset,
	}).Commit(c.cluster)
}
<|endoftext|>


<commit_before>package dht

import (
	"encoding/hex"
	"log"
	"net"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestDHTSec(t *testing.T) {
	for _, case_ := range []struct {
		ipStr     string
		nodeIDHex string
		valid     bool
	}{
		// These 5 are from the spec example. They are all valid.
		{"124.31.75.21", "5fbfbff10c5d6a4ec8a88e4c6ab4c28b95eee401", true},
		{"21.75.31.124", "5a3ce9c14e7a08645677bbd1cfe7d8f956d53256", true},
		{"65.23.51.170", "a5d43220bc8f112a3d426c84764f8c2a1150e616", true},
		{"84.124.73.14", "1b0321dd1bb1fe518101ceef99462b947a01ff41", true},
		{"43.213.53.83", "e56f6cbf5b7c4be0237986d5243b87aa6d51305a", true},
		// spec[0] with one of the rand() bytes changed. Valid.
		{"124.31.75.21", "5fbfbff10c5d7a4ec8a88e4c6ab4c28b95eee401", true},
		// spec[1] with the 21st leading bit changed. Not Valid.
		{"21.75.31.124", "5a3ce1c14e7a08645677bbd1cfe7d8f956d53256", false},
		// spec[2] with the 22nd leading bit changed. Valid.
		{"65.23.51.170", "a5d43620bc8f112a3d426c84764f8c2a1150e616", true},
		// spec[3] with the 4th last bit changed. Valid.
		{"84.124.73.14", "1b0321dd1bb1fe518101ceef99462b947a01fe01", true},
		// spec[4] with the 3rd last bit changed. Not valid.
		{"43.213.53.83", "e56f6cbf5b7c4be0237986d5243b87aa6d51303e", false},
		// Because class A network.
		{"10.213.53.83", "e56f6cbf5b7c4be0237986d5243b87aa6d51305a", true},
		// Because not class A, and id[0]&3 does not match.
		{"12.213.53.83", "e56f6cbf5b7c4be0237986d5243b87aa6d51305a", false},
		// Because class C.
		{"192.168.53.83", "e56f6cbf5b7c4be0237986d5243b87aa6d51305a", true},
	} {
		ip := net.ParseIP(case_.ipStr)
		_id, err := hex.DecodeString(case_.nodeIDHex)
		require.NoError(t, err)
		var id [20]byte
		require.Equal(t, 20, copy(id[:], _id))
		log.Printf("%q %q", id, _id)
		secure := NodeIdSecure(id, ip)
		assert.Equal(t, case_.valid, secure, "%v", case_)
		if !secure {
			// It's not secure, so secure it in place and then check it again.
			SecureNodeId(&id, ip)
			assert.True(t, NodeIdSecure(id, ip), "%v", case_)
		}
	}
}
<commit_msg>Test some assumptions around node ID security<commit_after>package dht

import (
	"crypto/rand"
	"encoding/hex"
	"log"
	"net"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestDHTSec(t *testing.T) {
	for _, case_ := range []struct {
		ipStr     string
		nodeIDHex string
		valid     bool
	}{
		// These 5 are from the spec example. They are all valid.
		{"124.31.75.21", "5fbfbff10c5d6a4ec8a88e4c6ab4c28b95eee401", true},
		{"21.75.31.124", "5a3ce9c14e7a08645677bbd1cfe7d8f956d53256", true},
		{"65.23.51.170", "a5d43220bc8f112a3d426c84764f8c2a1150e616", true},
		{"84.124.73.14", "1b0321dd1bb1fe518101ceef99462b947a01ff41", true},
		{"43.213.53.83", "e56f6cbf5b7c4be0237986d5243b87aa6d51305a", true},
		// spec[0] with one of the rand() bytes changed. Valid.
		{"124.31.75.21", "5fbfbff10c5d7a4ec8a88e4c6ab4c28b95eee401", true},
		// spec[1] with the 21st leading bit changed. Not Valid.
		{"21.75.31.124", "5a3ce1c14e7a08645677bbd1cfe7d8f956d53256", false},
		// spec[2] with the 22nd leading bit changed. Valid.
		{"65.23.51.170", "a5d43620bc8f112a3d426c84764f8c2a1150e616", true},
		// spec[3] with the 4th last bit changed. Valid.
		{"84.124.73.14", "1b0321dd1bb1fe518101ceef99462b947a01fe01", true},
		// spec[4] with the 3rd last bit changed. Not valid.
		{"43.213.53.83", "e56f6cbf5b7c4be0237986d5243b87aa6d51303e", false},
		// Because class A network.
		{"10.213.53.83", "e56f6cbf5b7c4be0237986d5243b87aa6d51305a", true},
		// Because not class A, and id[0]&3 does not match.
		{"12.213.53.83", "e56f6cbf5b7c4be0237986d5243b87aa6d51305a", false},
		// Because class C.
		{"192.168.53.83", "e56f6cbf5b7c4be0237986d5243b87aa6d51305a", true},
	} {
		ip := net.ParseIP(case_.ipStr)
		_id, err := hex.DecodeString(case_.nodeIDHex)
		require.NoError(t, err)
		var id [20]byte
		require.Equal(t, 20, copy(id[:], _id))
		log.Printf("%q %q", id, _id)
		secure := NodeIdSecure(id, ip)
		assert.Equal(t, case_.valid, secure, "%v", case_)
		if !secure {
			// It's not secure, so secure it in place and then check it again.
			SecureNodeId(&id, ip)
			assert.True(t, NodeIdSecure(id, ip), "%v", case_)
		}
	}
}

func getInsecureIp(nodeId [20]byte, ip net.IP) {
	for {
		rand.Read(ip)
		if !NodeIdSecure(nodeId, ip) {
			break
		}
	}
}

// Show that we can't secure a node ID against more than one IP.
func TestSecureNodeIdMultipleIps(t *testing.T) {
	id := RandomNodeID()
	t.Logf("random node id: %x", id)
	ip4 := make(net.IP, 4)
	getInsecureIp(id, ip4)
	ip6 := make(net.IP, 16)
	getInsecureIp(id, ip6)
	t.Logf("random ip4 address: %s", ip4)
	t.Logf("random ip6 address: %s", ip6)
	require.False(t, NodeIdSecure(id, ip4))
	require.False(t, NodeIdSecure(id, ip6))
	SecureNodeId(&id, ip4)
	assert.True(t, NodeIdSecure(id, ip4))
	assert.False(t, NodeIdSecure(id, ip6))
	SecureNodeId(&id, ip6)
	assert.True(t, NodeIdSecure(id, ip6))
	assert.False(t, NodeIdSecure(id, ip4))
}
<|endoftext|>


<commit_before>package sentry

import (
	"github.com/getsentry/sentry-go"
	"github.com/sirupsen/logrus"
)

var (
	levelsMap = map[logrus.Level]sentry.Level{
		logrus.PanicLevel: sentry.LevelFatal,
		logrus.FatalLevel: sentry.LevelFatal,
		logrus.ErrorLevel: sentry.LevelError,
		logrus.WarnLevel:  sentry.LevelWarning,
		logrus.InfoLevel:  sentry.LevelInfo,
		logrus.DebugLevel: sentry.LevelDebug,
		logrus.TraceLevel: sentry.LevelDebug,
	}
)

type Options sentry.ClientOptions

type Hook struct {
	client      *sentry.Client
	levels      []logrus.Level
	tags        map[string]string
	release     string
	environment string
}

func (hook *Hook) Levels() []logrus.Level {
	return hook.levels
}

func (hook *Hook) Fire(entry *logrus.Entry) error {
	exceptions := []sentry.Exception{}

	if err, ok := entry.Data[logrus.ErrorKey].(error); ok && err != nil {
		stacktrace := sentry.ExtractStacktrace(err)
		if stacktrace == nil {
			stacktrace = sentry.NewStacktrace()
		}
		exceptions = append(exceptions, sentry.Exception{
			Type:       entry.Message,
			Value:      err.Error(),
			Stacktrace: stacktrace,
		})
	}

	event := sentry.Event{
		Level:       levelsMap[entry.Level],
		Message:     entry.Message,
		Extra:       map[string]interface{}(entry.Data),
		Tags:        hook.tags,
		Environment: hook.environment,
		Release:     hook.release,
		Exception:   exceptions,
	}

	hub := sentry.CurrentHub()
	hook.client.CaptureEvent(&event, nil, hub.Scope())

	return nil
}

func (hook *Hook) SetTags(tags map[string]string) {
	hook.tags = tags
}

func (hook *Hook) AddTag(key, value string) {
	hook.tags[key] = value
}

func (hook *Hook) SetRelease(release string) {
	hook.release = release
}

func (hook *Hook) SetEnvironment(environment string) {
	hook.environment = environment
}

func NewHook(optinos Options, levels ...logrus.Level) (*Hook, error) {
	client, err := sentry.NewClient(sentry.ClientOptions(optinos))
	if err != nil {
		return nil, err
	}

	hook := Hook{
		client: client,
		levels: levels,
		tags:   map[string]string{},
	}
	if len(hook.levels) == 0 {
		hook.levels = logrus.AllLevels
	}

	return &hook, nil
}
<commit_msg>fix spelling mistake<commit_after>package sentry

import (
	"github.com/getsentry/sentry-go"
	"github.com/sirupsen/logrus"
)

var (
	levelsMap = map[logrus.Level]sentry.Level{
		logrus.PanicLevel: sentry.LevelFatal,
		logrus.FatalLevel: sentry.LevelFatal,
		logrus.ErrorLevel: sentry.LevelError,
		logrus.WarnLevel:  sentry.LevelWarning,
		logrus.InfoLevel:  sentry.LevelInfo,
		logrus.DebugLevel: sentry.LevelDebug,
		logrus.TraceLevel: sentry.LevelDebug,
	}
)

type Options sentry.ClientOptions

type Hook struct {
	client      *sentry.Client
	levels      []logrus.Level
	tags        map[string]string
	release     string
	environment string
}

func (hook *Hook) Levels() []logrus.Level {
	return hook.levels
}

func (hook *Hook) Fire(entry *logrus.Entry) error {
	exceptions := []sentry.Exception{}

	if err, ok := entry.Data[logrus.ErrorKey].(error); ok && err != nil {
		stacktrace := sentry.ExtractStacktrace(err)
		if stacktrace == nil {
			stacktrace = sentry.NewStacktrace()
		}
		exceptions = append(exceptions, sentry.Exception{
			Type:       entry.Message,
			Value:      err.Error(),
			Stacktrace: stacktrace,
		})
	}

	event := sentry.Event{
		Level:       levelsMap[entry.Level],
		Message:     entry.Message,
		Extra:       map[string]interface{}(entry.Data),
		Tags:        hook.tags,
		Environment: hook.environment,
		Release:     hook.release,
		Exception:   exceptions,
	}

	hub := sentry.CurrentHub()
	hook.client.CaptureEvent(&event, nil, hub.Scope())

	return nil
}

func (hook *Hook) SetTags(tags map[string]string) {
	hook.tags = tags
}

func (hook *Hook) AddTag(key, value string) {
	hook.tags[key] = value
}

func (hook *Hook) SetRelease(release string) {
	hook.release = release
}

func (hook *Hook) SetEnvironment(environment string) {
	hook.environment = environment
}

func NewHook(options Options, levels ...logrus.Level) (*Hook, error) {
	client, err := sentry.NewClient(sentry.ClientOptions(options))
	if err != nil {
		return nil, err
	}

	hook := Hook{
		client: client,
		levels: levels,
		tags:   map[string]string{},
	}
	if len(hook.levels) == 0 {
		hook.levels = logrus.AllLevels
	}

	return &hook, nil
}
<|endoftext|>


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

package server

import (
	"errors"
	"fmt"
)

var (
	// ErrConnectionClosed represents an error condition on a closed connection.
	ErrConnectionClosed = errors.New("connection closed")

	// ErrAuthentication represents an error condition on failed authentication.
	ErrAuthentication = errors.New("authentication error")

	// ErrAuthTimeout represents an error condition on failed authorization due to timeout.
	ErrAuthTimeout = errors.New("authentication timeout")

	// ErrAuthExpired represents an expired authorization due to timeout.
	ErrAuthExpired = errors.New("authentication expired")

	// ErrMaxPayload represents an error condition when the payload is too big.
	ErrMaxPayload = errors.New("maximum payload exceeded")

	// ErrMaxControlLine represents an error condition when the control line is too big.
	ErrMaxControlLine = errors.New("maximum control line exceeded")

	// ErrReservedPublishSubject represents an error condition when sending to a reserved subject, e.g. _SYS.>
	ErrReservedPublishSubject = errors.New("reserved internal subject")

	// ErrBadClientProtocol signals a client requested an invalud client protocol.
	ErrBadClientProtocol = errors.New("invalid client protocol")

	// ErrTooManyConnections signals a client that the maximum number of connections supported by the
	// server has been reached.
	ErrTooManyConnections = errors.New("maximum connections exceeded")

	// ErrTooManyAccountConnections signals that an acount has reached its maximum number of active
	// connections.
	ErrTooManyAccountConnections = errors.New("maximum account active connections exceeded")

	// ErrTooManySubs signals a client that the maximum number of subscriptions per connection
	// has been reached.
	ErrTooManySubs = errors.New("maximum subscriptions exceeded")

	// ErrClientConnectedToRoutePort represents an error condition when a client
	// attempted to connect to the route listen port.
	ErrClientConnectedToRoutePort = errors.New("attempted to connect to route port")

	// ErrClientConnectedToLeafNodePort represents an error condition when a client
	// attempted to connect to the leaf node listen port.
	ErrClientConnectedToLeafNodePort = errors.New("attempted to connect to leaf node port")

	// ErrAccountExists is returned when an account is attempted to be registered
	// but already exists.
	ErrAccountExists = errors.New("account exists")

	// ErrBadAccount represents a malformed or incorrect account.
	ErrBadAccount = errors.New("bad account")

	// ErrReservedAccount represents a reserved account that can not be created.
	ErrReservedAccount = errors.New("reserved account")

	// ErrMissingAccount is returned when an account does not exist.
	ErrMissingAccount = errors.New("account missing")

	// ErrAccountValidation is returned when an account has failed validation.
	ErrAccountValidation = errors.New("account validation failed")

	// ErrAccountExpired is returned when an account has expired.
	ErrAccountExpired = errors.New("account expired")

	// ErrNoAccountResolver is returned when we attempt an update but do not have an account resolver.
	ErrNoAccountResolver = errors.New("account resolver missing")

	// ErrAccountResolverUpdateTooSoon is returned when we attempt an update too soon to last request.
	ErrAccountResolverUpdateTooSoon = errors.New("account resolver update too soon")

	// ErrAccountResolverSameClaims is returned when same claims have been fetched.
	ErrAccountResolverSameClaims = errors.New("account resolver no new claims")

	// ErrStreamImportAuthorization is returned when a stream import is not authorized.
	ErrStreamImportAuthorization = errors.New("stream import not authorized")

	// ErrServiceImportAuthorization is returned when a service import is not authorized.
	ErrServiceImportAuthorization = errors.New("service import not authorized")

	// ErrClientOrRouteConnectedToGatewayPort represents an error condition when
	// a client or route attempted to connect to the Gateway port.
	ErrClientOrRouteConnectedToGatewayPort = errors.New("attempted to connect to gateway port")

	// ErrWrongGateway represents an error condition when a server receives a connect
	// request from a remote Gateway with a destination name that does not match the server's
	// Gateway's name.
	ErrWrongGateway = errors.New("wrong gateway")

	// ErrNoSysAccount is returned when an attempt to publish or subscribe is made
	// when there is no internal system account defined.
	ErrNoSysAccount = errors.New("system account not setup")

	// ErrRevocation is returned when a credential has been revoked.
	ErrRevocation = errors.New("credentials have been revoked")
)

// configErr is a configuration error.
type configErr struct {
	token  token
	reason string
}

// Source reports the location of a configuration error.
func (e *configErr) Source() string {
	return fmt.Sprintf("%s:%d:%d", e.token.SourceFile(), e.token.Line(), e.token.Position())
}

// Error reports the location and reason from a configuration error.
func (e *configErr) Error() string {
	if e.token != nil {
		return fmt.Sprintf("%s: %s", e.Source(), e.reason)
	}
	return e.reason
}

// unknownConfigFieldErr is an error reported in pedantic mode.
type unknownConfigFieldErr struct {
	configErr
	field string
}

// Error reports that an unknown field was in the configuration.
func (e *unknownConfigFieldErr) Error() string {
	return fmt.Sprintf("%s: unknown field %q", e.Source(), e.field)
}

// configWarningErr is an error reported in pedantic mode.
type configWarningErr struct {
	configErr
	field string
}

// Error reports a configuration warning.
func (e *configWarningErr) Error() string {
	return fmt.Sprintf("%s: invalid use of field %q: %s", e.Source(), e.field, e.reason)
}

// processConfigErr is the result of processing the configuration from the server.
type processConfigErr struct {
	errors   []error
	warnings []error
}

// Error returns the collection of errors separated by new lines,
// warnings appear first then hard errors.
func (e *processConfigErr) Error() string {
	var msg string
	for _, err := range e.Warnings() {
		msg += err.Error() + "\n"
	}
	for _, err := range e.Errors() {
		msg += err.Error() + "\n"
	}
	return msg
}

// Warnings returns the list of warnings.
func (e *processConfigErr) Warnings() []error {
	return e.warnings
}

// Errors returns the list of errors.
func (e *processConfigErr) Errors() []error {
	return e.errors
}
<commit_msg>cleanup: fix word errors in errors.go<commit_after>// Copyright 2012-2019 The NATS Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package server

import (
	"errors"
	"fmt"
)

var (
	// ErrConnectionClosed represents an error condition on a closed connection.
	ErrConnectionClosed = errors.New("connection closed")

	// ErrAuthentication represents an error condition on failed authentication.
	ErrAuthentication = errors.New("authentication error")

	// ErrAuthTimeout represents an error condition on failed authorization due to timeout.
	ErrAuthTimeout = errors.New("authentication timeout")

	// ErrAuthExpired represents an expired authorization due to timeout.
	ErrAuthExpired = errors.New("authentication expired")

	// ErrMaxPayload represents an error condition when the payload is too big.
	ErrMaxPayload = errors.New("maximum payload exceeded")

	// ErrMaxControlLine represents an error condition when the control line is too big.
	ErrMaxControlLine = errors.New("maximum control line exceeded")

	// ErrReservedPublishSubject represents an error condition when sending to a reserved subject, e.g. _SYS.>
	ErrReservedPublishSubject = errors.New("reserved internal subject")

	// ErrBadClientProtocol signals a client requested an invalid client protocol.
	ErrBadClientProtocol = errors.New("invalid client protocol")

	// ErrTooManyConnections signals a client that the maximum number of connections supported by the
	// server has been reached.
	ErrTooManyConnections = errors.New("maximum connections exceeded")

	// ErrTooManyAccountConnections signals that an account has reached its maximum number of active
	// connections.
	ErrTooManyAccountConnections = errors.New("maximum account active connections exceeded")

	// ErrTooManySubs signals a client that the maximum number of subscriptions per connection
	// has been reached.
	ErrTooManySubs = errors.New("maximum subscriptions exceeded")

	// ErrClientConnectedToRoutePort represents an error condition when a client
	// attempted to connect to the route listen port.
	ErrClientConnectedToRoutePort = errors.New("attempted to connect to route port")

	// ErrClientConnectedToLeafNodePort represents an error condition when a client
	// attempted to connect to the leaf node listen port.
	ErrClientConnectedToLeafNodePort = errors.New("attempted to connect to leaf node port")

	// ErrAccountExists is returned when an account is attempted to be registered
	// but already exists.
	ErrAccountExists = errors.New("account exists")

	// ErrBadAccount represents a malformed or incorrect account.
	ErrBadAccount = errors.New("bad account")

	// ErrReservedAccount represents a reserved account that can not be created.
	ErrReservedAccount = errors.New("reserved account")

	// ErrMissingAccount is returned when an account does not exist.
	ErrMissingAccount = errors.New("account missing")

	// ErrAccountValidation is returned when an account has failed validation.
	ErrAccountValidation = errors.New("account validation failed")

	// ErrAccountExpired is returned when an account has expired.
	ErrAccountExpired = errors.New("account expired")

	// ErrNoAccountResolver is returned when we attempt an update but do not have an account resolver.
	ErrNoAccountResolver = errors.New("account resolver missing")

	// ErrAccountResolverUpdateTooSoon is returned when we attempt an update too soon to last request.
	ErrAccountResolverUpdateTooSoon = errors.New("account resolver update too soon")

	// ErrAccountResolverSameClaims is returned when same claims have been fetched.
	ErrAccountResolverSameClaims = errors.New("account resolver no new claims")

	// ErrStreamImportAuthorization is returned when a stream import is not authorized.
	ErrStreamImportAuthorization = errors.New("stream import not authorized")

	// ErrServiceImportAuthorization is returned when a service import is not authorized.
	ErrServiceImportAuthorization = errors.New("service import not authorized")

	// ErrClientOrRouteConnectedToGatewayPort represents an error condition when
	// a client or route attempted to connect to the Gateway port.
	ErrClientOrRouteConnectedToGatewayPort = errors.New("attempted to connect to gateway port")

	// ErrWrongGateway represents an error condition when a server receives a connect
	// request from a remote Gateway with a destination name that does not match the server's
	// Gateway's name.
	ErrWrongGateway = errors.New("wrong gateway")

	// ErrNoSysAccount is returned when an attempt to publish or subscribe is made
	// when there is no internal system account defined.
	ErrNoSysAccount = errors.New("system account not setup")

	// ErrRevocation is returned when a credential has been revoked.
	ErrRevocation = errors.New("credentials have been revoked")
)

// configErr is a configuration error.
type configErr struct {
	token  token
	reason string
}

// Source reports the location of a configuration error.
func (e *configErr) Source() string {
	return fmt.Sprintf("%s:%d:%d", e.token.SourceFile(), e.token.Line(), e.token.Position())
}

// Error reports the location and reason from a configuration error.
func (e *configErr) Error() string {
	if e.token != nil {
		return fmt.Sprintf("%s: %s", e.Source(), e.reason)
	}
	return e.reason
}

// unknownConfigFieldErr is an error reported in pedantic mode.
type unknownConfigFieldErr struct {
	configErr
	field string
}

// Error reports that an unknown field was in the configuration.
func (e *unknownConfigFieldErr) Error() string {
	return fmt.Sprintf("%s: unknown field %q", e.Source(), e.field)
}

// configWarningErr is an error reported in pedantic mode.
type configWarningErr struct {
	configErr
	field string
}

// Error reports a configuration warning.
func (e *configWarningErr) Error() string {
	return fmt.Sprintf("%s: invalid use of field %q: %s", e.Source(), e.field, e.reason)
}

// processConfigErr is the result of processing the configuration from the server.
type processConfigErr struct {
	errors   []error
	warnings []error
}

// Error returns the collection of errors separated by new lines,
// warnings appear first then hard errors.
func (e *processConfigErr) Error() string {
	var msg string
	for _, err := range e.Warnings() {
		msg += err.Error() + "\n"
	}
	for _, err := range e.Errors() {
		msg += err.Error() + "\n"
	}
	return msg
}

// Warnings returns the list of warnings.
func (e *processConfigErr) Warnings() []error {
	return e.warnings
}

// Errors returns the list of errors.
func (e *processConfigErr) Errors() []error {
	return e.errors
}
<|endoftext|>


<commit_before>// Copyright 2017 Apcera Inc. All rights reserved.

package server

import (
	"crypto/tls"
	"errors"
	"fmt"
	"reflect"
	"strings"
)

// FlagSnapshot captures the server options as specified by CLI flags at
// startup. This should not be modified once the server has started.
var FlagSnapshot *Options

// option is a hot-swappable configuration setting.
type option interface {
	// Apply the server option.
	Apply(server *Server)
}

// traceOption implements the option interface for the `trace` setting.
type traceOption struct {
	newValue bool
}

// Apply the tracing change by reconfiguring the server's logger.
func (t *traceOption) Apply(server *Server) {
	server.ConfigureLogger()
	server.Noticef("Reloaded: trace = %v", t.newValue)
}

// debugOption implements the option interface for the `debug` setting.
type debugOption struct {
	newValue bool
}

// Apply the debug change by reconfiguring the server's logger.
func (d *debugOption) Apply(server *Server) {
	server.ConfigureLogger()
	server.Noticef("Reloaded: debug = %v", d.newValue)
}

// tlsOption implements the option interface for the `tls` setting.
type tlsOption struct {
	newValue *tls.Config
}

// Apply the tls change.
func (t *tlsOption) Apply(server *Server) {
	tlsRequired := t.newValue != nil
	server.info.TLSRequired = tlsRequired
	server.info.TLSVerify = tlsRequired && t.newValue.ClientAuth == tls.RequireAndVerifyClientCert
	server.generateServerInfoJSON()
	server.Noticef("Reloaded: tls")
}

// Reload reads the current configuration file and applies any supported
// changes. This returns an error if the server was not started with a config
// file or an option which doesn't support hot-swapping was changed.
func (s *Server) Reload() error {
	s.mu.Lock()
	defer s.mu.Unlock()

	if s.configFile == "" {
		return errors.New("Can only reload config when a file is provided using -c or --config")
	}
	newOpts, err := ProcessConfigFile(s.configFile)
	if err != nil {
		// TODO: Dump previous good config to a .bak file?
		return fmt.Errorf("Config reload failed: %s", err)
	}
	// Apply flags over config file settings.
	newOpts = MergeOptions(newOpts, FlagSnapshot)
	processOptions(newOpts)
	return s.reloadOptions(newOpts)
}

// reloadOptions reloads the server config with the provided options. If an
// option that doesn't support hot-swapping is changed, this returns an error.
func (s *Server) reloadOptions(newOpts *Options) error {
	changed, err := s.diffOptions(newOpts)
	if err != nil {
		return err
	}
	s.setOpts(newOpts)
	s.applyOptions(changed)
	return nil
}

// diffOptions returns a slice containing options which have been changed. If
// an option that doesn't support hot-swapping is changed, this returns an
// error.
func (s *Server) diffOptions(newOpts *Options) ([]option, error) {
	var (
		oldConfig = reflect.ValueOf(s.getOpts()).Elem()
		newConfig = reflect.ValueOf(newOpts).Elem()
		diffOpts  = []option{}
	)

	for i := 0; i < oldConfig.NumField(); i++ {
		var (
			field    = oldConfig.Type().Field(i)
			oldValue = oldConfig.Field(i).Interface()
			newValue = newConfig.Field(i).Interface()
			changed  = !reflect.DeepEqual(oldValue, newValue)
		)
		if !changed {
			continue
		}
		switch strings.ToLower(field.Name) {
		case "trace":
			diffOpts = append(diffOpts, &traceOption{newValue.(bool)})
		case "debug":
			diffOpts = append(diffOpts, &debugOption{newValue.(bool)})
		case "tlsconfig":
			diffOpts = append(diffOpts, &tlsOption{newValue.(*tls.Config)})
		default:
			// Bail out if attempting to reload any unsupported options.
			return nil, fmt.Errorf("Config reload not supported for %s", field.Name)
		}
	}

	return diffOpts, nil
}

func (s *Server) applyOptions(opts []option) {
	for _, opt := range opts {
		opt.Apply(s)
	}

	s.Noticef("Reloaded server configuration")
}
<commit_msg>Improve readability of TLSVerify assignment<commit_after>// Copyright 2017 Apcera Inc. All rights reserved.

package server

import (
	"crypto/tls"
	"errors"
	"fmt"
	"reflect"
	"strings"
)

// FlagSnapshot captures the server options as specified by CLI flags at
// startup. This should not be modified once the server has started.
var FlagSnapshot *Options

// option is a hot-swappable configuration setting.
type option interface {
	// Apply the server option.
	Apply(server *Server)
}

// traceOption implements the option interface for the `trace` setting.
type traceOption struct {
	newValue bool
}

// Apply the tracing change by reconfiguring the server's logger.
func (t *traceOption) Apply(server *Server) {
	server.ConfigureLogger()
	server.Noticef("Reloaded: trace = %v", t.newValue)
}

// debugOption implements the option interface for the `debug` setting.
type debugOption struct {
	newValue bool
}

// Apply the debug change by reconfiguring the server's logger.
func (d *debugOption) Apply(server *Server) {
	server.ConfigureLogger()
	server.Noticef("Reloaded: debug = %v", d.newValue)
}

// tlsOption implements the option interface for the `tls` setting.
type tlsOption struct {
	newValue *tls.Config
}

// Apply the tls change.
func (t *tlsOption) Apply(server *Server) {
	tlsRequired := t.newValue != nil
	server.info.TLSRequired = tlsRequired
	if tlsRequired {
		server.info.TLSVerify = (t.newValue.ClientAuth == tls.RequireAndVerifyClientCert)
	}
	server.generateServerInfoJSON()
	server.Noticef("Reloaded: tls")
}

// Reload reads the current configuration file and applies any supported
// changes. This returns an error if the server was not started with a config
// file or an option which doesn't support hot-swapping was changed.
func (s *Server) Reload() error {
	s.mu.Lock()
	defer s.mu.Unlock()

	if s.configFile == "" {
		return errors.New("Can only reload config when a file is provided using -c or --config")
	}
	newOpts, err := ProcessConfigFile(s.configFile)
	if err != nil {
		// TODO: Dump previous good config to a .bak file?
		return fmt.Errorf("Config reload failed: %s", err)
	}
	// Apply flags over config file settings.
	newOpts = MergeOptions(newOpts, FlagSnapshot)
	processOptions(newOpts)
	return s.reloadOptions(newOpts)
}

// reloadOptions reloads the server config with the provided options. If an
// option that doesn't support hot-swapping is changed, this returns an error.
func (s *Server) reloadOptions(newOpts *Options) error {
	changed, err := s.diffOptions(newOpts)
	if err != nil {
		return err
	}
	s.setOpts(newOpts)
	s.applyOptions(changed)
	return nil
}

// diffOptions returns a slice containing options which have been changed. If
// an option that doesn't support hot-swapping is changed, this returns an
// error.
func (s *Server) diffOptions(newOpts *Options) ([]option, error) {
	var (
		oldConfig = reflect.ValueOf(s.getOpts()).Elem()
		newConfig = reflect.ValueOf(newOpts).Elem()
		diffOpts  = []option{}
	)

	for i := 0; i < oldConfig.NumField(); i++ {
		var (
			field    = oldConfig.Type().Field(i)
			oldValue = oldConfig.Field(i).Interface()
			newValue = newConfig.Field(i).Interface()
			changed  = !reflect.DeepEqual(oldValue, newValue)
		)
		if !changed {
			continue
		}
		switch strings.ToLower(field.Name) {
		case "trace":
			diffOpts = append(diffOpts, &traceOption{newValue.(bool)})
		case "debug":
			diffOpts = append(diffOpts, &debugOption{newValue.(bool)})
		case "tlsconfig":
			diffOpts = append(diffOpts, &tlsOption{newValue.(*tls.Config)})
		default:
			// Bail out if attempting to reload any unsupported options.
			return nil, fmt.Errorf("Config reload not supported for %s", field.Name)
		}
	}

	return diffOpts, nil
}

func (s *Server) applyOptions(opts []option) {
	for _, opt := range opts {
		opt.Apply(s)
	}

	s.Noticef("Reloaded server configuration")
}
<|endoftext|>


<commit_before>package server

var routerTemplateStr = `
package server

// Code auto-generated. Do not edit.

import (
	"compress/gzip"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"path"
	"strconv"
	"time"

	// register pprof listener
	_ "net/http/pprof"

	"github.com/gorilla/handlers"
	"github.com/gorilla/mux"
	opentracing "github.com/opentracing/opentracing-go"
	"gopkg.in/Clever/kayvee-go.v6/logger"
	kvMiddleware "gopkg.in/Clever/kayvee-go.v6/middleware"
	"gopkg.in/tylerb/graceful.v1"
	"github.com/Clever/go-process-metrics/metrics"
	"github.com/kardianos/osext"
	jaeger "github.com/uber/jaeger-client-go"
	jaegercfg "github.com/uber/jaeger-client-go/config"
	"github.com/uber/jaeger-client-go/transport"
)

const (
	// lowerBoundRateLimiter determines the lower bound interval that we sample every operation.
	// https://godoc.org/github.com/uber/jaeger-client-go#GuaranteedThroughputProbabilisticSampler
	lowerBoundRateLimiter = 1.0 / 60 // 1 request/minute/operation
)

type contextKey struct{}

// Server defines a HTTP server that implements the Controller interface.
type Server struct {
	// Handler should generally not be changed. It exposed to make testing easier.
	Handler http.Handler
	addr string
	l logger.KayveeLogger
	config serverConfig
}

type serverConfig struct{
	compressionLevel int
}

func CompressionLevel(level int) func(*serverConfig) {
	return func(c *serverConfig) {
		c.compressionLevel = level
	}
}

// Serve starts the server. It will return if an error occurs.
func (s *Server) Serve() error {
	tracingToken := os.Getenv("TRACING_ACCESS_TOKEN")
	ingestURL := os.Getenv("TRACING_INGEST_URL")
	isLocal := os.Getenv("_IS_LOCAL") == "true"

	if !isLocal {
		go startLoggingProcessMetrics()
	}

	go func() {
		// This should never return. Listen on the pprof port
		log.Printf("PProf server crashed: %%s", http.ListenAndServe("localhost:6060", nil))
	}()

	dir, err := osext.ExecutableFolder()
	if err != nil {
		log.Fatal(err)
	}
	if err := logger.SetGlobalRouting(path.Join(dir, "kvconfig.yml")); err != nil {
		s.l.Info("please provide a kvconfig.yml file to enable app log routing")
	}

	if (tracingToken != "" && ingestURL != "") || isLocal {
		samplingRate := .01 // 1%% of requests

		if samplingRateStr := os.Getenv("TRACING_SAMPLING_RATE_PERCENT"); samplingRateStr != "" {
			samplingRateP, err := strconv.ParseFloat(samplingRateStr, 64)
			if err != nil {
				s.l.ErrorD("tracing-sampling-override-failed", logger.M{
					"msg": fmt.Sprintf("could not parse '%%s' to integer", samplingRateStr),
				})
			} else {
				samplingRate = samplingRateP
			}

			s.l.InfoD("tracing-sampling-rate", logger.M{
				"msg": fmt.Sprintf("sampling rate will be %%.3f", samplingRate),
			})
		}

		sampler, err := jaeger.NewGuaranteedThroughputProbabilisticSampler(lowerBoundRateLimiter, samplingRate)
		if err != nil {
			return fmt.Errorf("failed to build jaeger sampler: %%s", err)
		}

		cfg := &jaegercfg.Configuration{
			ServiceName: os.Getenv("_APP_NAME"),
			Tags:        []opentracing.Tag{
				opentracing.Tag{Key: "app_name", Value: os.Getenv("_APP_NAME")},
				opentracing.Tag{Key: "build_id", Value: os.Getenv("_BUILD_ID")},
				opentracing.Tag{Key: "deploy_env", Value: os.Getenv("_DEPLOY_ENV")},
				opentracing.Tag{Key: "team_owner", Value: os.Getenv("_TEAM_OWNER")},
				opentracing.Tag{Key: "pod_id", Value: os.Getenv("_POD_ID")},
				opentracing.Tag{Key: "pod_shortname", Value: os.Getenv("_POD_SHORTNAME")},
				opentracing.Tag{Key: "pod_account", Value: os.Getenv("_POD_ACCOUNT")},
				opentracing.Tag{Key: "pod_region", Value: os.Getenv("_POD_REGION")},
			},
		}

		var tracer opentracing.Tracer
		var closer io.Closer
		if isLocal {
			// when local, send everything and use the default params for the Jaeger collector
			cfg.Sampler = &jaegercfg.SamplerConfig{
				Type:  "const",
				Param: 1.0,
			}
			tracer, closer, err = cfg.NewTracer()
			s.l.InfoD("local-tracing", logger.M{"msg": "sending traces to default localhost jaeger address"})
		} else {
			// Create a Jaeger HTTP Thrift transport
			transport := transport.NewHTTPTransport(ingestURL, transport.HTTPBasicAuth("auth", tracingToken))
			tracer, closer, err = cfg.NewTracer(
				jaegercfg.Reporter(jaeger.NewRemoteReporter(transport)),
				jaegercfg.Sampler(sampler))
		}
		if err != nil {
			log.Fatalf("Could not initialize jaeger tracer: %%s", err)
		}
		defer closer.Close()

		opentracing.SetGlobalTracer(tracer)
	} else {
		s.l.Error("please set TRACING_ACCESS_TOKEN & TRACING_INGEST_URL to enable tracing")
	}

	s.l.Counter("server-started")

	// Give the sever 30 seconds to shut down
	return graceful.RunWithErr(s.addr,30*time.Second,s.Handler)
}

type handler struct {
	Controller
}

func startLoggingProcessMetrics() {
	metrics.Log("{{.Title}}", 1*time.Minute)
}

func withMiddleware(serviceName string, router http.Handler, m []func(http.Handler) http.Handler, config serverConfig) http.Handler {
	handler := router

	// compress everything
	handler = handlers.CompressHandlerLevel(handler, config.compressionLevel)

	// Wrap the middleware in the opposite order specified so that when called then run
	// in the order specified
	for i := len(m) - 1; i >= 0; i-- {
		handler = m[i](handler)
	}
	handler = TracingMiddleware(handler)
	handler = PanicMiddleware(handler)
	// Logging middleware comes last, i.e. will be run first.
	// This makes it so that other middleware has access to the logger
	// that kvMiddleware injects into the request context.
	handler = kvMiddleware.New(handler, serviceName)
	return handler
}

// New returns a Server that implements the Controller interface. It will start when "Serve" is called.
func New(c Controller, addr string, options ...func(*serverConfig)) *Server {
	return NewWithMiddleware(c, addr, []func(http.Handler) http.Handler{}, options...)
}

// NewRouter returns a mux.Router with no middleware. This is so we can attach additional routes to the
// router if necessary
func NewRouter(c Controller) *mux.Router {
	return newRouter(c)
}

func newRouter(c Controller) *mux.Router {
	router := mux.NewRouter()
	h := handler{Controller: c}

	{{range $index, $val := .Functions}}
	router.Methods("{{$val.Method}}").Path("{{$val.Path}}").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		logger.FromContext(r.Context()).AddContext("op", "{{$val.OpID}}")
		h.{{$val.HandlerName}}Handler(r.Context(), w, r)
		ctx := WithTracingOpName(r.Context(), "{{$val.OpID}}")
		r = r.WithContext(ctx)
	})
	{{end}}
	return router
}

// NewWithMiddleware returns a Server that implemenets the Controller interface. It runs the
// middleware after the built-in middleware (e.g. logging), but before the controller methods.
// The middleware is executed in the order specified. The server will start when "Serve" is called.
func NewWithMiddleware(c Controller, addr string, m []func(http.Handler) http.Handler, options ...func(*serverConfig)) *Server {
	router := newRouter(c)

	return AttachMiddleware(router, addr, m, options...)
}

// AttachMiddleware attaches the given middleware to the router; this is to be used in conjunction with
// NewServer. It attaches custom middleware passed as arguments as well as the built-in middleware for
// logging, tracing, and handling panics. It should be noted that the built-in middleware executes first
// followed by the passed in middleware (in the order specified).
func AttachMiddleware(router *mux.Router, addr string, m []func(http.Handler) http.Handler, options ...func(*serverConfig)) *Server {
	// Set sane defaults, to be overriden by the varargs functions.
	// This would probably be better done in NewWithMiddleware, but there are services that call
	// AttachMiddleWare directly instead.
	config := serverConfig {
		compressionLevel: gzip.DefaultCompression,
	}
	for _, option := range options {
		option(&config)
	}

	l := logger.New("{{.Title}}")

	handler := withMiddleware("{{.Title}}", router, m, config)
	return &Server{Handler: handler, addr: addr, l: l, config: config}
}`
<commit_msg>handroll graceful server shutdown<commit_after>package server

var routerTemplateStr = `
package server

// Code auto-generated. Do not edit.

import (
	"compress/gzip"
	"context"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"os/signal"
	"path"
	"strconv"
	"syscall"
	"time"

	// register pprof listener
	_ "net/http/pprof"

	"github.com/gorilla/handlers"
	"github.com/gorilla/mux"
	opentracing "github.com/opentracing/opentracing-go"
	"gopkg.in/Clever/kayvee-go.v6/logger"
	kvMiddleware "gopkg.in/Clever/kayvee-go.v6/middleware"
	"github.com/Clever/go-process-metrics/metrics"
	"github.com/kardianos/osext"
	jaeger "github.com/uber/jaeger-client-go"
	jaegercfg "github.com/uber/jaeger-client-go/config"
	"github.com/uber/jaeger-client-go/transport"
)

const (
	// lowerBoundRateLimiter determines the lower bound interval that we sample every operation.
	// https://godoc.org/github.com/uber/jaeger-client-go#GuaranteedThroughputProbabilisticSampler
	lowerBoundRateLimiter = 1.0 / 60 // 1 request/minute/operation
)

type contextKey struct{}

// Server defines a HTTP server that implements the Controller interface.
type Server struct {
	// Handler should generally not be changed. It exposed to make testing easier.
	Handler http.Handler
	addr string
	l logger.KayveeLogger
	config serverConfig
}

type serverConfig struct{
	compressionLevel int
}

func CompressionLevel(level int) func(*serverConfig) {
	return func(c *serverConfig) {
		c.compressionLevel = level
	}
}

// Serve starts the server. It will return if an error occurs.
func (s *Server) Serve() error {
	tracingToken := os.Getenv("TRACING_ACCESS_TOKEN")
	ingestURL := os.Getenv("TRACING_INGEST_URL")
	isLocal := os.Getenv("_IS_LOCAL") == "true"

	if !isLocal {
		go startLoggingProcessMetrics()
	}

	go func() {
		// This should never return. Listen on the pprof port
		log.Printf("PProf server crashed: %%s", http.ListenAndServe("localhost:6060", nil))
	}()

	dir, err := osext.ExecutableFolder()
	if err != nil {
		log.Fatal(err)
	}
	if err := logger.SetGlobalRouting(path.Join(dir, "kvconfig.yml")); err != nil {
		s.l.Info("please provide a kvconfig.yml file to enable app log routing")
	}

	if (tracingToken != "" && ingestURL != "") || isLocal {
		samplingRate := .01 // 1%% of requests

		if samplingRateStr := os.Getenv("TRACING_SAMPLING_RATE_PERCENT"); samplingRateStr != "" {
			samplingRateP, err := strconv.ParseFloat(samplingRateStr, 64)
			if err != nil {
				s.l.ErrorD("tracing-sampling-override-failed", logger.M{
					"msg": fmt.Sprintf("could not parse '%%s' to integer", samplingRateStr),
				})
			} else {
				samplingRate = samplingRateP
			}

			s.l.InfoD("tracing-sampling-rate", logger.M{
				"msg": fmt.Sprintf("sampling rate will be %%.3f", samplingRate),
			})
		}

		sampler, err := jaeger.NewGuaranteedThroughputProbabilisticSampler(lowerBoundRateLimiter, samplingRate)
		if err != nil {
			return fmt.Errorf("failed to build jaeger sampler: %%s", err)
		}

		cfg := &jaegercfg.Configuration{
			ServiceName: os.Getenv("_APP_NAME"),
			Tags:        []opentracing.Tag{
				opentracing.Tag{Key: "app_name", Value: os.Getenv("_APP_NAME")},
				opentracing.Tag{Key: "build_id", Value: os.Getenv("_BUILD_ID")},
				opentracing.Tag{Key: "deploy_env", Value: os.Getenv("_DEPLOY_ENV")},
				opentracing.Tag{Key: "team_owner", Value: os.Getenv("_TEAM_OWNER")},
				opentracing.Tag{Key: "pod_id", Value: os.Getenv("_POD_ID")},
				opentracing.Tag{Key: "pod_shortname", Value: os.Getenv("_POD_SHORTNAME")},
				opentracing.Tag{Key: "pod_account", Value: os.Getenv("_POD_ACCOUNT")},
				opentracing.Tag{Key: "pod_region", Value: os.Getenv("_POD_REGION")},
			},
		}

		var tracer opentracing.Tracer
		var closer io.Closer
		if isLocal {
			// when local, send everything and use the default params for the Jaeger collector
			cfg.Sampler = &jaegercfg.SamplerConfig{
				Type:  "const",
				Param: 1.0,
			}
			tracer, closer, err = cfg.NewTracer()
			s.l.InfoD("local-tracing", logger.M{"msg": "sending traces to default localhost jaeger address"})
		} else {
			// Create a Jaeger HTTP Thrift transport
			transport := transport.NewHTTPTransport(ingestURL, transport.HTTPBasicAuth("auth", tracingToken))
			tracer, closer, err = cfg.NewTracer(
				jaegercfg.Reporter(jaeger.NewRemoteReporter(transport)),
				jaegercfg.Sampler(sampler))
		}
		if err != nil {
			log.Fatalf("Could not initialize jaeger tracer: %%s", err)
		}
		defer closer.Close()

		opentracing.SetGlobalTracer(tracer)
	} else {
		s.l.Error("please set TRACING_ACCESS_TOKEN & TRACING_INGEST_URL to enable tracing")
	}

	s.l.Counter("server-started")

	// Give the sever 30 seconds to shut down
	server := &http.Server{
		Addr:        s.addr,
		Handler:     s.Handler,
		IdleTimeout: 3 * time.Minute,
	}
	server.SetKeepAlivesEnabled(true)

	// Give the server 30 seconds to shut down gracefully after it receives a signal
	shutdown := make(chan struct{})
	go func() {
		c := make(chan os.Signal, 1)
		signal.Notify(c, os.Interrupt, os.Signal(syscall.SIGTERM))
		sig := <-c
		s.l.CriticalD("shutdown-initiated", logger.M{"signal": sig.String()})
		ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
		defer cancel()
		defer close(shutdown)
		if err := server.Shutdown(ctx); err != nil {
			s.l.CriticalD("error-during-shutdown", logger.M{"error": err.Error()})
		}
	}()

	if err := server.ListenAndServe(); err != http.ErrServerClosed {
		return err
	}
	// ensure we wait for graceful shutdown
	<-shutdown

	return nil
}

type handler struct {
	Controller
}

func startLoggingProcessMetrics() {
	metrics.Log("{{.Title}}", 1*time.Minute)
}

func withMiddleware(serviceName string, router http.Handler, m []func(http.Handler) http.Handler, config serverConfig) http.Handler {
	handler := router

	// compress everything
	handler = handlers.CompressHandlerLevel(handler, config.compressionLevel)

	// Wrap the middleware in the opposite order specified so that when called then run
	// in the order specified
	for i := len(m) - 1; i >= 0; i-- {
		handler = m[i](handler)
	}
	handler = TracingMiddleware(handler)
	handler = PanicMiddleware(handler)
	// Logging middleware comes last, i.e. will be run first.
	// This makes it so that other middleware has access to the logger
	// that kvMiddleware injects into the request context.
	handler = kvMiddleware.New(handler, serviceName)
	return handler
}

// New returns a Server that implements the Controller interface. It will start when "Serve" is called.
func New(c Controller, addr string, options ...func(*serverConfig)) *Server {
	return NewWithMiddleware(c, addr, []func(http.Handler) http.Handler{}, options...)
}

// NewRouter returns a mux.Router with no middleware. This is so we can attach additional routes to the
// router if necessary
func NewRouter(c Controller) *mux.Router {
	return newRouter(c)
}

func newRouter(c Controller) *mux.Router {
	router := mux.NewRouter()
	h := handler{Controller: c}

	{{range $index, $val := .Functions}}
	router.Methods("{{$val.Method}}").Path("{{$val.Path}}").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		logger.FromContext(r.Context()).AddContext("op", "{{$val.OpID}}")
		h.{{$val.HandlerName}}Handler(r.Context(), w, r)
		ctx := WithTracingOpName(r.Context(), "{{$val.OpID}}")
		r = r.WithContext(ctx)
	})
	{{end}}
	return router
}

// NewWithMiddleware returns a Server that implemenets the Controller interface. It runs the
// middleware after the built-in middleware (e.g. logging), but before the controller methods.
// The middleware is executed in the order specified. The server will start when "Serve" is called.
func NewWithMiddleware(c Controller, addr string, m []func(http.Handler) http.Handler, options ...func(*serverConfig)) *Server {
	router := newRouter(c)

	return AttachMiddleware(router, addr, m, options...)
}

// AttachMiddleware attaches the given middleware to the router; this is to be used in conjunction with
// NewServer. It attaches custom middleware passed as arguments as well as the built-in middleware for
// logging, tracing, and handling panics. It should be noted that the built-in middleware executes first
// followed by the passed in middleware (in the order specified).
func AttachMiddleware(router *mux.Router, addr string, m []func(http.Handler) http.Handler, options ...func(*serverConfig)) *Server {
	// Set sane defaults, to be overriden by the varargs functions.
	// This would probably be better done in NewWithMiddleware, but there are services that call
	// AttachMiddleWare directly instead.
	config := serverConfig {
		compressionLevel: gzip.DefaultCompression,
	}
	for _, option := range options {
		option(&config)
	}

	l := logger.New("{{.Title}}")

	handler := withMiddleware("{{.Title}}", router, m, config)
	return &Server{Handler: handler, addr: addr, l: l, config: config}
}`
<|endoftext|>