<commit_before>package density

import (
	"log"

	"github.com/phil-mansfield/gotetra/render/geom"
	"github.com/phil-mansfield/gotetra/math/rand"
)

type Interpolator interface {
	Interpolate(
		buf Buffer, xs, vs []geom.Vec,
		ptVal float64, weights Buffer,
		low, high, jump int,
	)

	// The bounding box around the box being written to.
	DomainCellBounds() *geom.CellBounds
	// The bounding box around the box being written from.
	BufferCellBounds() *geom.CellBounds

	Cells() int
}

type ngp struct { }

// The ordering of these fields makes no goddamned sense.
type mcarlo struct {
	subIntr Interpolator
	segWidth int64
	points, cells int

	gen *rand.Generator

	skip int64
	// Buffers
	idxBuf geom.TetraIdxs
	tet geom.Tetra
	vtet geom.Tetra

	unitBufs [][]geom.Vec
	vecBuf []geom.Vec
}

func MonteCarlo(
	segWidth int64,
	points, cells int,
	skip int64,
	unitBufs [][]geom.Vec,
	subIntr Interpolator,
) Interpolator {
	mc := &mcarlo{
		subIntr, segWidth, points, cells,
		rand.NewTimeSeed(rand.Xorshift), skip,
		geom.TetraIdxs{}, geom.Tetra{}, geom.Tetra{},
		unitBufs, make([]geom.Vec, points),
	}

	return mc
}

func (intr *mcarlo) DomainCellBounds() *geom.CellBounds {
	return intr.subIntr.DomainCellBounds()
}

func (intr *mcarlo) BufferCellBounds() *geom.CellBounds {
	return intr.subIntr.BufferCellBounds()
}

func (intr *mcarlo) Cells() int {
	return intr.subIntr.Cells()
}

func (intr *mcarlo) Interpolate(
	buf Buffer, xs, vs []geom.Vec,
	ptVal float64, weights Buffer,
	low, high, jump int,
) {
	// weights will contain the velocity if needed and will be a NilBuffer
	// otherwise.

	segWidth := intr.segWidth
	gridWidth := segWidth + 1
	idxWidth := intr.segWidth / intr.skip

	reqVel := buf.Quantity().RequiresVelocity()
	if !reqVel {
		ptVal = ptVal / float64(intr.points) / 6.0 *
			float64(intr.skip * intr.skip * intr.skip)
	}

	tetCb := &geom.CellBounds{}

	relCb := &geom.CellBounds{}
	relCb.Width = intr.DomainCellBounds().Width
	relCb.Origin[0], relCb.Origin[1], relCb.Origin[2] =
		cbSubtr(intr.DomainCellBounds(), intr.BufferCellBounds())

	jump64 := int64(jump)

	// I hate this so much. I hate it so much:
	//
	// (Also, I have no idea how it works.)
	neverMod := *intr.subIntr.DomainCellBounds() !=
		*intr.subIntr.BufferCellBounds() ||
		(intr.Cells() / 2 > relCb.Width[0] &&
		intr.Cells() / 2 > relCb.Width[1] &&
		intr.Cells() / 2 > relCb.Width[2])

	maxWidth := 0.0

	for idx := int64(low); idx < int64(high); idx += jump64 {
		x, y, z := coords(idx, idxWidth)
		gridIdx := index(x * intr.skip, y * intr.skip, z * intr.skip, gridWidth)
		for dir := 0; dir < 6; dir++ {
			intr.idxBuf.Init(gridIdx, gridWidth, intr.skip, dir)

			intr.tet.Init(
				&xs[intr.idxBuf[0]],
				&xs[intr.idxBuf[1]],
				&xs[intr.idxBuf[2]],
				&xs[intr.idxBuf[3]],
			)

			if width(&intr.tet) > maxWidth {
				maxWidth = width(&intr.tet)
			}

			if reqVel {
				intr.vtet.Init(
					&vs[intr.idxBuf[0]],
					&vs[intr.idxBuf[1]],
					&vs[intr.idxBuf[2]],
					&vs[intr.idxBuf[3]],
				)
			}

			intr.tet.CellBoundsAt(1.0, tetCb)

			if !tetCb.Intersect(relCb, intr.cells) { continue }

			bufIdx := intr.gen.UniformInt(0, len(intr.unitBufs))
			intr.tet.DistributeTetra(
				intr.unitBufs[bufIdx],
				intr.vecBuf,
			)

			// Lol, whatever.
			if !neverMod {
				for j := 0; j < 3; j++ {
					if coordNeg(&intr.tet, j) {
						modCoord(intr.vecBuf, j, float32(intr.Cells()))
					}
				}
			}

			var bweights Buffer
			switch b := buf.(type) {
			case *densityBuffer, *gradientBuffer:
				bweights = NilBuffer
			case *velocityBuffer:
				wbuf, ok := b.weights.VectorBuffer()
				if !ok { panic("buf is non-vector when vector is required.") }
				intr.vtet.DistributeTetra64(intr.unitBufs[bufIdx], wbuf)
				bweights = b.weights
			case *curlBuffer:
				wbuf, ok := b.weights.VectorBuffer()
				if !ok { panic("buf is non-vector when vector is required.") }
				intr.vtet.DistributeTetra64(intr.unitBufs[bufIdx], wbuf)
				bweights = b.weights
			case *divergenceBuffer:
				wbuf, ok := b.weights.VectorBuffer()
				if !ok { panic("buf is non-vector when vector is required.") }
				intr.vtet.DistributeTetra64(intr.unitBufs[bufIdx], wbuf)
				bweights = b.weights
			}

			intr.subIntr.Interpolate(
				buf, intr.vecBuf, nil, ptVal, bweights, 0, intr.points, 1,
			)
		}
	}

	if maxWidth > 1000 {
		log.Printf(
			"width: %.5g, bound: %v\n", maxWidth,
			intr.subIntr.BufferCellBounds(),
		)
	}
}

func coordNeg(tet *geom.Tetra, j int) bool {
	for i := 0; i < 4; i++ {
		if tet.Corners[i][j] < 0 { return true }
	}
	return false
}

func modCoord(buf []geom.Vec, j int, width float32) {
	for i := range buf {
		if buf[i][j] < 0 { buf[i][j] += width }
	}
}

func index(x, y, z, cells int64) int64 {
	return x + y * cells + z * cells * cells
}

func coords(idx, cells int64) (x, y, z int64) {
	x = idx % cells
	y = (idx % (cells * cells)) / cells
	z = idx / (cells * cells)
	return x, y, z
}

func cbSubtr(cb1, cb2 *geom.CellBounds) (i, j, k int) {
	i = cb1.Origin[0] - cb2.Origin[0]
	j = cb1.Origin[1] - cb2.Origin[1]
	k = cb1.Origin[2] - cb2.Origin[2]
	return i, j, k
}

func width(tet *geom.Tetra) float64 {
	minX, maxX := tet.Corners[0][0], tet.Corners[0][0]
	for i := 1; i < 4; i++ {
		x := tet.Corners[i][0]
		if x < minX { minX = x } else if x > maxX { maxX = x }
	}
	return float64(maxX - minX)
}
<commit_msg>Removed a logging statement that I no longer remember the point of.<commit_after>package density

import (
	"log"

	"github.com/phil-mansfield/gotetra/render/geom"
	"github.com/phil-mansfield/gotetra/math/rand"
)

type Interpolator interface {
	Interpolate(
		buf Buffer, xs, vs []geom.Vec,
		ptVal float64, weights Buffer,
		low, high, jump int,
	)

	// The bounding box around the box being written to.
	DomainCellBounds() *geom.CellBounds
	// The bounding box around the box being written from.
	BufferCellBounds() *geom.CellBounds

	Cells() int
}

type ngp struct { }

// The ordering of these fields makes no goddamned sense.
type mcarlo struct {
	subIntr Interpolator
	segWidth int64
	points, cells int

	gen *rand.Generator

	skip int64
	// Buffers
	idxBuf geom.TetraIdxs
	tet geom.Tetra
	vtet geom.Tetra

	unitBufs [][]geom.Vec
	vecBuf []geom.Vec
}

func MonteCarlo(
	segWidth int64,
	points, cells int,
	skip int64,
	unitBufs [][]geom.Vec,
	subIntr Interpolator,
) Interpolator {
	mc := &mcarlo{
		subIntr, segWidth, points, cells,
		rand.NewTimeSeed(rand.Xorshift), skip,
		geom.TetraIdxs{}, geom.Tetra{}, geom.Tetra{},
		unitBufs, make([]geom.Vec, points),
	}

	return mc
}

func (intr *mcarlo) DomainCellBounds() *geom.CellBounds {
	return intr.subIntr.DomainCellBounds()
}

func (intr *mcarlo) BufferCellBounds() *geom.CellBounds {
	return intr.subIntr.BufferCellBounds()
}

func (intr *mcarlo) Cells() int {
	return intr.subIntr.Cells()
}

func (intr *mcarlo) Interpolate(
	buf Buffer, xs, vs []geom.Vec,
	ptVal float64, weights Buffer,
	low, high, jump int,
) {
	// weights will contain the velocity if needed and will be a NilBuffer
	// otherwise.

	segWidth := intr.segWidth
	gridWidth := segWidth + 1
	idxWidth := intr.segWidth / intr.skip

	reqVel := buf.Quantity().RequiresVelocity()
	if !reqVel {
		ptVal = ptVal / float64(intr.points) / 6.0 *
			float64(intr.skip * intr.skip * intr.skip)
	}

	tetCb := &geom.CellBounds{}

	relCb := &geom.CellBounds{}
	relCb.Width = intr.DomainCellBounds().Width
	relCb.Origin[0], relCb.Origin[1], relCb.Origin[2] =
		cbSubtr(intr.DomainCellBounds(), intr.BufferCellBounds())

	jump64 := int64(jump)

	// I hate this so much. I hate it so much:
	//
	// (Also, I have no idea how it works.)
	neverMod := *intr.subIntr.DomainCellBounds() !=
		*intr.subIntr.BufferCellBounds() ||
		(intr.Cells() / 2 > relCb.Width[0] &&
		intr.Cells() / 2 > relCb.Width[1] &&
		intr.Cells() / 2 > relCb.Width[2])

	maxWidth := 0.0

	for idx := int64(low); idx < int64(high); idx += jump64 {
		x, y, z := coords(idx, idxWidth)
		gridIdx := index(x * intr.skip, y * intr.skip, z * intr.skip, gridWidth)
		for dir := 0; dir < 6; dir++ {
			intr.idxBuf.Init(gridIdx, gridWidth, intr.skip, dir)

			intr.tet.Init(
				&xs[intr.idxBuf[0]],
				&xs[intr.idxBuf[1]],
				&xs[intr.idxBuf[2]],
				&xs[intr.idxBuf[3]],
			)

			if width(&intr.tet) > maxWidth {
				maxWidth = width(&intr.tet)
			}

			if reqVel {
				intr.vtet.Init(
					&vs[intr.idxBuf[0]],
					&vs[intr.idxBuf[1]],
					&vs[intr.idxBuf[2]],
					&vs[intr.idxBuf[3]],
				)
			}

			intr.tet.CellBoundsAt(1.0, tetCb)

			if !tetCb.Intersect(relCb, intr.cells) { continue }

			bufIdx := intr.gen.UniformInt(0, len(intr.unitBufs))
			intr.tet.DistributeTetra(
				intr.unitBufs[bufIdx],
				intr.vecBuf,
			)

			// Lol, whatever.
			if !neverMod {
				for j := 0; j < 3; j++ {
					if coordNeg(&intr.tet, j) {
						modCoord(intr.vecBuf, j, float32(intr.Cells()))
					}
				}
			}

			var bweights Buffer
			switch b := buf.(type) {
			case *densityBuffer, *gradientBuffer:
				bweights = NilBuffer
			case *velocityBuffer:
				wbuf, ok := b.weights.VectorBuffer()
				if !ok { panic("buf is non-vector when vector is required.") }
				intr.vtet.DistributeTetra64(intr.unitBufs[bufIdx], wbuf)
				bweights = b.weights
			case *curlBuffer:
				wbuf, ok := b.weights.VectorBuffer()
				if !ok { panic("buf is non-vector when vector is required.") }
				intr.vtet.DistributeTetra64(intr.unitBufs[bufIdx], wbuf)
				bweights = b.weights
			case *divergenceBuffer:
				wbuf, ok := b.weights.VectorBuffer()
				if !ok { panic("buf is non-vector when vector is required.") }
				intr.vtet.DistributeTetra64(intr.unitBufs[bufIdx], wbuf)
				bweights = b.weights
			}

			intr.subIntr.Interpolate(
				buf, intr.vecBuf, nil, ptVal, bweights, 0, intr.points, 1,
			)
		}
	}
}

func coordNeg(tet *geom.Tetra, j int) bool {
	for i := 0; i < 4; i++ {
		if tet.Corners[i][j] < 0 { return true }
	}
	return false
}

func modCoord(buf []geom.Vec, j int, width float32) {
	for i := range buf {
		if buf[i][j] < 0 { buf[i][j] += width }
	}
}

func index(x, y, z, cells int64) int64 {
	return x + y * cells + z * cells * cells
}

func coords(idx, cells int64) (x, y, z int64) {
	x = idx % cells
	y = (idx % (cells * cells)) / cells
	z = idx / (cells * cells)
	return x, y, z
}

func cbSubtr(cb1, cb2 *geom.CellBounds) (i, j, k int) {
	i = cb1.Origin[0] - cb2.Origin[0]
	j = cb1.Origin[1] - cb2.Origin[1]
	k = cb1.Origin[2] - cb2.Origin[2]
	return i, j, k
}

func width(tet *geom.Tetra) float64 {
	minX, maxX := tet.Corners[0][0], tet.Corners[0][0]
	for i := 1; i < 4; i++ {
		x := tet.Corners[i][0]
		if x < minX { minX = x } else if x > maxX { maxX = x }
	}
	return float64(maxX - minX)
}
<|endoftext|>


<commit_before>package main

// The git commit that was compiled. This will be filled in by the compiler.
var GitCommit string

// The main version number that is being run at the moment.
const Version = "0.3.2"

// A pre-release marker for the version. If this is "" (empty string)
// then it means that it is a final release. Otherwise, this is a pre-release
// such as "dev" (in development), "beta", "rc1", etc.
const VersionPrerelease = ""
<commit_msg>0.3.3<commit_after>package main

// The git commit that was compiled. This will be filled in by the compiler.
var GitCommit string

// The main version number that is being run at the moment.
const Version = "0.3.3"

// A pre-release marker for the version. If this is "" (empty string)
// then it means that it is a final release. Otherwise, this is a pre-release
// such as "dev" (in development), "beta", "rc1", etc.
const VersionPrerelease = ""
<|endoftext|>


<commit_before>// Copyright (c) 2015 Uber Technologies, Inc.

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

package tchannel

// VersionInfo identifies the version of the TChannel library.
// Due to lack of proper package management, this version string will
// be maintained manually.
const VersionInfo = "1.14.0"
<commit_msg>Back to development<commit_after>// Copyright (c) 2015 Uber Technologies, Inc.

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

package tchannel

// VersionInfo identifies the version of the TChannel library.
// Due to lack of proper package management, this version string will
// be maintained manually.
const VersionInfo = "1.14.0-dev"
<|endoftext|>


<commit_before>package gf

const VERSION = "v1.11.5"
const AUTHORS = "john<john@goframe.org>"
<commit_msg>version updates<commit_after>package gf

const VERSION = "v1.11.6"
const AUTHORS = "john<john@goframe.org>"
<|endoftext|>


<commit_before>// +build windows

package vhd

import "syscall"

//go:generate go run mksyscall_windows.go -output zvhd.go vhd.go

//sys createVirtualDisk(virtualStorageType *virtualStorageType, path string, virtualDiskAccessMask uint32, securityDescriptor *uintptr, flags uint32, providerSpecificFlags uint32, parameters *createVirtualDiskParameters, o *syscall.Overlapped, handle *syscall.Handle) (err error) [failretval != 0] = VirtDisk.CreateVirtualDisk
//sys openVirtualDisk(virtualStorageType *virtualStorageType, path string, virtualDiskAccessMask uint32, flags uint32, parameters *openVirtualDiskParameters, handle *syscall.Handle) (err error) [failretval != 0] = VirtDisk.OpenVirtualDisk
//sys detachVirtualDisk(handle syscall.Handle, flags uint32, providerSpecificFlags uint32) (err error) [failretval != 0] = VirtDisk.DetachVirtualDisk

type virtualStorageType struct {
	DeviceID uint32
	VendorID [16]byte
}

type (
	createVirtualDiskFlag uint32
	VirtualDiskAccessMask uint32
	VirtualDiskFlag       uint32
)

const (
	// Flags for creating a VHD (not exported)
	createVirtualDiskFlagNone                        createVirtualDiskFlag = 0
	createVirtualDiskFlagFullPhysicalAllocation      createVirtualDiskFlag = 1
	createVirtualDiskFlagPreventWritesToSourceDisk   createVirtualDiskFlag = 2
	createVirtualDiskFlagDoNotCopyMetadataFromParent createVirtualDiskFlag = 4

	// Access Mask for opening a VHD
	VirtualDiskAccessNone     VirtualDiskAccessMask = 0
	VirtualDiskAccessAttachRO VirtualDiskAccessMask = 65536
	VirtualDiskAccessAttachRW VirtualDiskAccessMask = 131072
	VirtualDiskAccessDetach   VirtualDiskAccessMask = 262144
	VirtualDiskAccessGetInfo  VirtualDiskAccessMask = 524288
	VirtualDiskAccessCreate   VirtualDiskAccessMask = 1048576
	VirtualDiskAccessMetaOps  VirtualDiskAccessMask = 2097152
	VirtualDiskAccessRead     VirtualDiskAccessMask = 851968
	VirtualDiskAccessAll      VirtualDiskAccessMask = 4128768
	VirtualDiskAccessWritable VirtualDiskAccessMask = 3276800

	// Flags for opening a VHD
	OpenVirtualDiskFlagNone                        VirtualDiskFlag = 0
	OpenVirtualDiskFlagNoParents                   VirtualDiskFlag = 0x1
	OpenVirtualDiskFlagBlankFile                   VirtualDiskFlag = 0x2
	OpenVirtualDiskFlagBootDrive                   VirtualDiskFlag = 0x4
	OpenVirtualDiskFlagCachedIO                    VirtualDiskFlag = 0x8
	OpenVirtualDiskFlagCustomDiffChain             VirtualDiskFlag = 0x10
	OpenVirtualDiskFlagParentCachedIO              VirtualDiskFlag = 0x20
	OpenVirtualDiskFlagVhdSetFileOnly              VirtualDiskFlag = 0x40
	OpenVirtualDiskFlagIgnoreRelativeParentLocator VirtualDiskFlag = 0x80
	OpenVirtualDiskFlagNoWriteHardening            VirtualDiskFlag = 0x100
)

type createVersion2 struct {
	UniqueID                 [16]byte // GUID
	MaximumSize              uint64
	BlockSizeInBytes         uint32
	SectorSizeInBytes        uint32
	ParentPath               *uint16 // string
	SourcePath               *uint16 // string
	OpenFlags                uint32
	ParentVirtualStorageType virtualStorageType
	SourceVirtualStorageType virtualStorageType
	ResiliencyGUID           [16]byte // GUID
}

type createVirtualDiskParameters struct {
	Version  uint32 // Must always be set to 2
	Version2 createVersion2
}

type openVersion2 struct {
	GetInfoOnly    int32    // bool but 4-byte aligned
	ReadOnly       int32    // bool but 4-byte aligned
	ResiliencyGUID [16]byte // GUID
}

type openVirtualDiskParameters struct {
	Version  uint32 // Must always be set to 2
	Version2 openVersion2
}

// CreateVhdx will create a simple vhdx file at the given path using default values.
func CreateVhdx(path string, maxSizeInGb, blockSizeInMb uint32) error {
	var (
		defaultType virtualStorageType
		handle      syscall.Handle
	)

	parameters := createVirtualDiskParameters{
		Version: 2,
		Version2: createVersion2{
			MaximumSize:      uint64(maxSizeInGb) * 1024 * 1024 * 1024,
			BlockSizeInBytes: blockSizeInMb * 1024 * 1024,
		},
	}

	if err := createVirtualDisk(
		&defaultType,
		path,
		uint32(VirtualDiskAccessNone),
		nil,
		uint32(createVirtualDiskFlagNone),
		0,
		&parameters,
		nil,
		&handle); err != nil {
		return err
	}

	if err := syscall.CloseHandle(handle); err != nil {
		return err
	}

	return nil
}

// DetachVhd detaches a VHD attached at the given path.
func DetachVhd(path string) error {
	handle, err := OpenVirtualDisk(path, VirtualDiskAccessDetach, OpenVirtualDiskFlagNone)
	if err != nil {
		return err
	}
	defer syscall.CloseHandle(handle)
	return detachVirtualDisk(handle, 0, 0)
}

// OpenVirtuaDisk obtains a handle to a VHD opened with supplied access mask and flags.
func OpenVirtualDisk(path string, accessMask VirtualDiskAccessMask, flag VirtualDiskFlag) (syscall.Handle, error) {
	var (
		defaultType virtualStorageType
		handle      syscall.Handle
	)
	parameters := openVirtualDiskParameters{Version: 2}
	if err := openVirtualDisk(
		&defaultType,
		path,
		uint32(accessMask),
		uint32(flag),
		&parameters,
		&handle); err != nil {
		return 0, err
	}
	return handle, nil
}
<commit_msg>Fix regression in DetachVhd<commit_after>// +build windows

package vhd

import "syscall"

//go:generate go run mksyscall_windows.go -output zvhd.go vhd.go

//sys createVirtualDisk(virtualStorageType *virtualStorageType, path string, virtualDiskAccessMask uint32, securityDescriptor *uintptr, flags uint32, providerSpecificFlags uint32, parameters *createVirtualDiskParameters, o *syscall.Overlapped, handle *syscall.Handle) (err error) [failretval != 0] = VirtDisk.CreateVirtualDisk
//sys openVirtualDisk(virtualStorageType *virtualStorageType, path string, virtualDiskAccessMask uint32, flags uint32, parameters *openVirtualDiskParameters, handle *syscall.Handle) (err error) [failretval != 0] = VirtDisk.OpenVirtualDisk
//sys detachVirtualDisk(handle syscall.Handle, flags uint32, providerSpecificFlags uint32) (err error) [failretval != 0] = VirtDisk.DetachVirtualDisk

type virtualStorageType struct {
	DeviceID uint32
	VendorID [16]byte
}

type (
	createVirtualDiskFlag uint32
	VirtualDiskAccessMask uint32
	VirtualDiskFlag       uint32
)

const (
	// Flags for creating a VHD (not exported)
	createVirtualDiskFlagNone                        createVirtualDiskFlag = 0
	createVirtualDiskFlagFullPhysicalAllocation      createVirtualDiskFlag = 1
	createVirtualDiskFlagPreventWritesToSourceDisk   createVirtualDiskFlag = 2
	createVirtualDiskFlagDoNotCopyMetadataFromParent createVirtualDiskFlag = 4

	// Access Mask for opening a VHD
	VirtualDiskAccessNone     VirtualDiskAccessMask = 0
	VirtualDiskAccessAttachRO VirtualDiskAccessMask = 65536
	VirtualDiskAccessAttachRW VirtualDiskAccessMask = 131072
	VirtualDiskAccessDetach   VirtualDiskAccessMask = 262144
	VirtualDiskAccessGetInfo  VirtualDiskAccessMask = 524288
	VirtualDiskAccessCreate   VirtualDiskAccessMask = 1048576
	VirtualDiskAccessMetaOps  VirtualDiskAccessMask = 2097152
	VirtualDiskAccessRead     VirtualDiskAccessMask = 851968
	VirtualDiskAccessAll      VirtualDiskAccessMask = 4128768
	VirtualDiskAccessWritable VirtualDiskAccessMask = 3276800

	// Flags for opening a VHD
	OpenVirtualDiskFlagNone                        VirtualDiskFlag = 0
	OpenVirtualDiskFlagNoParents                   VirtualDiskFlag = 0x1
	OpenVirtualDiskFlagBlankFile                   VirtualDiskFlag = 0x2
	OpenVirtualDiskFlagBootDrive                   VirtualDiskFlag = 0x4
	OpenVirtualDiskFlagCachedIO                    VirtualDiskFlag = 0x8
	OpenVirtualDiskFlagCustomDiffChain             VirtualDiskFlag = 0x10
	OpenVirtualDiskFlagParentCachedIO              VirtualDiskFlag = 0x20
	OpenVirtualDiskFlagVhdSetFileOnly              VirtualDiskFlag = 0x40
	OpenVirtualDiskFlagIgnoreRelativeParentLocator VirtualDiskFlag = 0x80
	OpenVirtualDiskFlagNoWriteHardening            VirtualDiskFlag = 0x100
)

type createVersion2 struct {
	UniqueID                 [16]byte // GUID
	MaximumSize              uint64
	BlockSizeInBytes         uint32
	SectorSizeInBytes        uint32
	ParentPath               *uint16 // string
	SourcePath               *uint16 // string
	OpenFlags                uint32
	ParentVirtualStorageType virtualStorageType
	SourceVirtualStorageType virtualStorageType
	ResiliencyGUID           [16]byte // GUID
}

type createVirtualDiskParameters struct {
	Version  uint32 // Must always be set to 2
	Version2 createVersion2
}

type openVersion2 struct {
	GetInfoOnly    int32    // bool but 4-byte aligned
	ReadOnly       int32    // bool but 4-byte aligned
	ResiliencyGUID [16]byte // GUID
}

type openVirtualDiskParameters struct {
	Version  uint32 // Must always be set to 2
	Version2 openVersion2
}

// CreateVhdx will create a simple vhdx file at the given path using default values.
func CreateVhdx(path string, maxSizeInGb, blockSizeInMb uint32) error {
	var (
		defaultType virtualStorageType
		handle      syscall.Handle
	)

	parameters := createVirtualDiskParameters{
		Version: 2,
		Version2: createVersion2{
			MaximumSize:      uint64(maxSizeInGb) * 1024 * 1024 * 1024,
			BlockSizeInBytes: blockSizeInMb * 1024 * 1024,
		},
	}

	if err := createVirtualDisk(
		&defaultType,
		path,
		uint32(VirtualDiskAccessNone),
		nil,
		uint32(createVirtualDiskFlagNone),
		0,
		&parameters,
		nil,
		&handle); err != nil {
		return err
	}

	if err := syscall.CloseHandle(handle); err != nil {
		return err
	}

	return nil
}

// DetachVhd detaches a mounted container layer vhd found at `path`.
func DetachVhd(path string) error {
	handle, err := OpenVirtualDisk(
		path,
		VirtualDiskAccessNone,
		OpenVirtualDiskFlagCachedIO|OpenVirtualDiskFlagIgnoreRelativeParentLocator)

	if err != nil {
		return err
	}
	defer syscall.CloseHandle(handle)
	return detachVirtualDisk(handle, 0, 0)
}

// OpenVirtualDisk obtains a handle to a VHD opened with supplied access mask and flags.
func OpenVirtualDisk(path string, accessMask VirtualDiskAccessMask, flag VirtualDiskFlag) (syscall.Handle, error) {
	var (
		defaultType virtualStorageType
		handle      syscall.Handle
	)
	parameters := openVirtualDiskParameters{Version: 2}
	if err := openVirtualDisk(
		&defaultType,
		path,
		uint32(accessMask),
		uint32(flag),
		&parameters,
		&handle); err != nil {
		return 0, err
	}
	return handle, nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"os"
	"time"

	"github.com/jessevdk/go-flags"
	"github.com/mackerelio/checkers"
)

type healthStat struct {
	ClusterName string `json:"cluster_name"`
	Status      string `json:"status"`
}

var opts struct {
	Host string `short:"H" long:"host" default:"localhost" description:"Elasticsearch host"`
	Port int64  `short:"p" long:"port" default:"9200" description:"Elasticsearch port"`
}

func main() {
	ckr := run(os.Args[1:])
	ckr.Name = "Elasticsearch"
	ckr.Exit()
}

func run(args []string) *checkers.Checker {
	_, err := flags.ParseArgs(&opts, args)
	if err != nil {
		os.Exit(1)
	}

	client := &http.Client{}
	url := fmt.Sprintf("http://%s:%d/_cluster/health", opts.Host, opts.Port)

	stTime := time.Now()
	resp, err := client.Get(url)
	if err != nil {
		return checkers.Critical(err.Error())
	}
	elapsed := time.Since(stTime)
	defer resp.Body.Close()

	dec := json.NewDecoder(resp.Body)
	var health healthStat
	dec.Decode(&health)

	checkSt := checkers.UNKNOWN
	switch health.Status {
	case "green":
		checkSt = checkers.OK
	case "yellow":
		checkSt = checkers.WARNING
	case "red":
		checkSt = checkers.CRITICAL
	default:
		checkSt = checkers.UNKNOWN
	}

	msg := fmt.Sprintf("%s (cluster: %s) - %f second respons time",
		health.Status, health.ClusterName, elapsed.Seconds())

	return checkers.NewChecker(checkSt, msg)
}
<commit_msg>add scheme option<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"os"
	"time"

	"github.com/jessevdk/go-flags"
	"github.com/mackerelio/checkers"
)

type healthStat struct {
	ClusterName string `json:"cluster_name"`
	Status      string `json:"status"`
}

var opts struct {
	Scheme string `short:"s" long:"scheme" default:"http" description:"Elasticsearch scheme"`
	Host   string `short:"H" long:"host" default:"localhost" description:"Elasticsearch host"`
	Port   int64  `short:"p" long:"port" default:"9200" description:"Elasticsearch port"`
}

func main() {
	ckr := run(os.Args[1:])
	ckr.Name = "Elasticsearch"
	ckr.Exit()
}

func run(args []string) *checkers.Checker {
	_, err := flags.ParseArgs(&opts, args)
	if err != nil {
		os.Exit(1)
	}

	client := &http.Client{}
	url := fmt.Sprintf("%s://%s:%d/_cluster/health", opts.Scheme, opts.Host, opts.Port)

	stTime := time.Now()
	resp, err := client.Get(url)
	if err != nil {
		return checkers.Critical(err.Error())
	}
	elapsed := time.Since(stTime)
	defer resp.Body.Close()

	dec := json.NewDecoder(resp.Body)
	var health healthStat
	dec.Decode(&health)

	checkSt := checkers.UNKNOWN
	switch health.Status {
	case "green":
		checkSt = checkers.OK
	case "yellow":
		checkSt = checkers.WARNING
	case "red":
		checkSt = checkers.CRITICAL
	default:
		checkSt = checkers.UNKNOWN
	}

	msg := fmt.Sprintf("%s (cluster: %s) - %f second respons time",
		health.Status, health.ClusterName, elapsed.Seconds())

	return checkers.NewChecker(checkSt, msg)
}
<|endoftext|>


<commit_before>package libcentrifugo

import (
	"encoding/json"
	"errors"
	"github.com/shilkin/centrifugo/libcentrifugo/logger"
	"github.com/tarantool/go-tarantool"
	"strconv"
	"strings"
)

func (p *TarantoolPool) get() (conn *tarantool.Connection, err error) {
	if len(p.pool) == 0 {
		return nil, errors.New("Empty tarantool pool")
	}
	conn = p.pool[p.current]
	p.current++
	p.current = (p.current) % len(p.pool)
	return
}

type TarantoolEngine struct {
	app      *Application
	pool     *TarantoolPool
	endpoint string
}

type TarantoolEngineConfig struct {
	PoolConfig TarantoolPoolConfig
	Endpoint   string
}

type TarantoolPool struct {
	pool    []*tarantool.Connection
	config  TarantoolPoolConfig
	current int
}

type TarantoolPoolConfig struct {
	Address  string
	PoolSize int
	Opts     tarantool.Opts
}

/* MessageType
{
	"body": {
		"uid":"026c380d-13e1-47d9-42d2-e2dc0e41e8d5",
		"timestamp":"1440434259",
		"info":{
			"user":"3",
			"client":"83309b33-deb7-48ff-76c6-04b10e6a6523",
			"default_info":null,
			"channel_info": {
				"channel_extra_info_example":"you can add additional JSON data when authorizing"
			}
		},
		"channel":"$3_0",
		"data": {
				"Action":"mark",
				"Data":["00000000000000395684"]
			},
		"client":"83309b33-deb7-48ff-76c6-04b10e6a6523"
	},
	"error":null,
	"method":"message"
}
*/

type MessageType struct {
	Body   Message
	Error  string `json:error`
	Method string `json:method`
}

type ServiceMessage struct {
	Action string
	Data   []string
}

type IDs []string

func NewTarantoolEngine(app *Application, conf TarantoolEngineConfig) *TarantoolEngine {
	logger.INFO.Printf("Initializing tarantool connection pool...")
	pool, err := newTarantoolPool(conf.PoolConfig)
	if err != nil {
		logger.FATAL.Fatalln(err)
	}

	e := &TarantoolEngine{
		app:      app,
		pool:     pool,
		endpoint: conf.Endpoint,
	}

	return e
}

func newTarantoolPool(config TarantoolPoolConfig) (p *TarantoolPool, err error) {
	if config.PoolSize == 0 {
		return nil, errors.New("Size of tarantool pool is zero")
	}

	p = &TarantoolPool{
		pool:   make([]*tarantool.Connection, config.PoolSize),
		config: config,
	}

	for i := 0; i < config.PoolSize; i++ {
		logger.INFO.Printf("[%d] Connecting to tarantool on %s...", i, config.Address)
		p.pool[i], err = tarantool.Connect(config.Address, config.Opts)
		if err != nil {
			return
		}
		logger.INFO.Printf("[%d] Connected to tarantool on %s", i, config.Address)
	}

	return p, nil
}

// getName returns a name of concrete engine implementation
func (e *TarantoolEngine) name() string {
	return "Tarantool"
}

// publish allows to send message into channel
func (e *TarantoolEngine) publish(chID ChannelID, message []byte) error {
	/*
		message:
			action: mark, push
			params:	[id,...]
	*/

	// Process service messages
	if chID != e.app.config.ControlChannel && chID != e.app.config.AdminChannel {
		if further, err := e.processMessage(chID, message); !further {
			return err // if no need further processing
		}
	}
	// All other messages
	return e.app.handleMsg(chID, message)
}

// subscribe on channel
func (e *TarantoolEngine) subscribe(chID ChannelID) (err error) {
	uid, ringno, project, err := parseChannelID(chID)
	if err != nil {
		return
	}

	conn, err := e.pool.get()
	if err != nil {
		logger.ERROR.Printf("subscribe tarantool pool error: %v\n", err.Error())
		return
	}

	_, err = conn.Call("notification_subscribe", []interface{}{uid, ringno, e.endpoint + "/api/" + project})

	return
}

// unsubscribe from channel
func (e *TarantoolEngine) unsubscribe(chID ChannelID) (err error) {
	uid, ringno, project, err := parseChannelID(chID)
	if err != nil {
		return
	}

	conn, err := e.pool.get()
	if err != nil {
		logger.ERROR.Printf("unsubscribe tarantool pool error: %v\n", err.Error())
		return
	}

	_, err = conn.Call("notification_unsubscribe", []interface{}{uid, ringno, e.endpoint + "/api/" + project})

	return
}

// addPresence sets or updates presence info for connection with uid
func (e *TarantoolEngine) addPresence(chID ChannelID, uid ConnID, info ClientInfo) (err error) {
	// not implemented
	return
}

// removePresence removes presence information for connection with uid
func (e *TarantoolEngine) removePresence(chID ChannelID, uid ConnID) (err error) {
	// not implemented
	return
}

// getPresence returns actual presence information for channel
func (e *TarantoolEngine) presence(chID ChannelID) (result map[ConnID]ClientInfo, err error) {
	// not implemented
	return
}

// addHistory adds message into channel history and takes care about history size
func (e *TarantoolEngine) addHistory(chID ChannelID, message Message, size, lifetime int64) (err error) {
	// not implemented
	return
}

// getHistory returns history messages for channel
// return empty slice
// all history pushed via publish
func (e *TarantoolEngine) history(chID ChannelID) (msgs []Message, err error) {
	uid, ringno, _, err := parseChannelID(chID)
	if err != nil {
		logger.ERROR.Printf("history parse chID error: %v\n", err.Error())
		return nil, err
	}

	conn, err := e.pool.get()
	if err != nil {
		logger.ERROR.Printf("history tarantool pool error: %v\n", err.Error())
		return nil, err
	}

	history, err := conn.Call("notification_read", []interface{}{uid, ringno})
	if err != nil {
		logger.ERROR.Printf("history notification_read error: %v\n", err.Error())
		return nil, err
	}

	return processHistory(history)
}

// helpers

type tarantoolHistoryItem struct {
	Count  interface{} `json:count`
	Status string      `json:status`
	ID     string      `json:id`
}

func processHistory(history *tarantool.Response) (msgs []Message, err error) {
	if len(history.Data) == 0 {
		return // history is empty
	}

	data := history.Data[0].([]interface{})
	if len(data) != 2 {
		return // history is empty
	}

	count := data[0]						// ring counter
	buffer := data[1].(string)				// string buffer
	ring := strings.Split(buffer[1:], ",")	// array of IDs

	if len(ring) == 0 {
		return	// history buffer is empty [useless?]
	}

	for _, id := range ring {
		encoded, err := json.Marshal(tarantoolHistoryItem{
			Count:  count, // redundancy in each item to pass number of unread notificatins
			Status: string(id[0]),
			ID:     string(id[1:]),
		})
		if err != nil {
			logger.ERROR.Println(err)
			continue
		}
		rawMessage := json.RawMessage([]byte(encoded))
		msgs = append(msgs, Message{Data: &rawMessage})
	}

	return
}

func (e *TarantoolEngine) processMessage(chID ChannelID, message []byte) (needFurtherProcessing bool, err error) {
	var msg MessageType
	err = json.Unmarshal(message, &msg)
	if err != nil {
		return true, err
	}

	var srv ServiceMessage
	err = json.Unmarshal(*msg.Body.Data, &srv)
	if err != nil {
		return true, err
	}

	if srv.Action == "" {
		return true, nil
	}

	var functionName string
	switch srv.Action {
	case "mark":
		functionName = "notification_mark"
	case "push":
		functionName = "notification_push"
	default:
		return true, nil
	}

	var uid, ringno int64
	uid, ringno, _, err = parseChannelID(chID)
	if err != nil {
		return
	}

	var conn *tarantool.Connection
	conn, err = e.pool.get()
	if err != nil {
		return
	}

	for _, id := range srv.Data {
		_, err = conn.Call(functionName, []interface{}{uid, ringno, id})
		if err != nil {
			logger.ERROR.Printf("%s call error: %s", functionName, err)
			return
		}
	}

	return
}

func parseChannelID(chID ChannelID) (uid, ringno int64, project string, err error) {
	// split chID <centrifugo>.<project>.[$]<uid>_<ringno>
	str := string(chID)
	logger.DEBUG.Printf("parseChannelID %s", str)

	result := strings.Split(str, ".")
	if len(result) != 3 {
		logger.DEBUG.Printf("unexpected ChannelID %s", str)
		return
	}

	project = result[1]
	str = result[2]

	separator := "_"
	prefix := "$"

	if strings.HasPrefix(str, prefix) {
		str = strings.TrimLeft(str, prefix)
	}
	channel := strings.Split(str, separator)

	uid, err = strconv.ParseInt(channel[0], 10, 64)
	if err != nil {
		return
	}
	ringno, err = strconv.ParseInt(channel[1], 10, 64)
	if err != nil {
		return
	}
	return
}
<commit_msg>MAILPAAS-1248| Blocking connect to each node of tarantool from pool<commit_after>package libcentrifugo

import (
	"encoding/json"
	"errors"
	"github.com/shilkin/centrifugo/libcentrifugo/logger"
	"github.com/tarantool/go-tarantool"
	"strconv"
	"strings"
	"time"
)

func (p *TarantoolPool) get() (conn *tarantool.Connection, err error) {
	if len(p.pool) == 0 {
		return nil, errors.New("Empty tarantool pool")
	}
	conn = p.pool[p.current]
	p.current++
	p.current = (p.current) % len(p.pool)
	return
}

type TarantoolEngine struct {
	app      *Application
	pool     *TarantoolPool
	endpoint string
}

type TarantoolEngineConfig struct {
	PoolConfig TarantoolPoolConfig
	Endpoint   string
}

type TarantoolPool struct {
	pool    []*tarantool.Connection
	config  TarantoolPoolConfig
	current int
}

type TarantoolPoolConfig struct {
	Address  string
	PoolSize int
	Opts     tarantool.Opts
}

/* MessageType
{
	"body": {
		"uid":"026c380d-13e1-47d9-42d2-e2dc0e41e8d5",
		"timestamp":"1440434259",
		"info":{
			"user":"3",
			"client":"83309b33-deb7-48ff-76c6-04b10e6a6523",
			"default_info":null,
			"channel_info": {
				"channel_extra_info_example":"you can add additional JSON data when authorizing"
			}
		},
		"channel":"$3_0",
		"data": {
				"Action":"mark",
				"Data":["00000000000000395684"]
			},
		"client":"83309b33-deb7-48ff-76c6-04b10e6a6523"
	},
	"error":null,
	"method":"message"
}
*/

type MessageType struct {
	Body   Message
	Error  string `json:error`
	Method string `json:method`
}

type ServiceMessage struct {
	Action string
	Data   []string
}

type IDs []string

func NewTarantoolEngine(app *Application, conf TarantoolEngineConfig) *TarantoolEngine {
	logger.INFO.Printf("Initializing tarantool connection pool...")
	pool, err := newTarantoolPool(conf.PoolConfig)
	if err != nil {
		logger.FATAL.Fatalln(err)
	}

	e := &TarantoolEngine{
		app:      app,
		pool:     pool,
		endpoint: conf.Endpoint,
	}

	return e
}

func newTarantoolPool(config TarantoolPoolConfig) (p *TarantoolPool, err error) {
	if config.PoolSize == 0 {
		return nil, errors.New("Size of tarantool pool is zero")
	}

	p = &TarantoolPool{
		pool:   make([]*tarantool.Connection, config.PoolSize),
		config: config,
	}

	for i := 0; i < config.PoolSize; i++ {
		logger.INFO.Printf("[%d] Connecting to tarantool on %s...", i, config.Address)
		for recons := 0; recons < config.Opts.MaxReconnects; recons++ {
			// try until success
			p.pool[i], err = tarantool.Connect(config.Address, config.Opts)
			if err != nil {
				err = p.pool[i].Close()
				if err != nil {
					logger.ERROR.Print(err)
				}
				time.Sleep(config.Opts.Reconnect)
				continue // on error
			}
			break // on success
		}
		logger.INFO.Printf("[%d] Connected to tarantool on %s", i, config.Address)
	}

	return p, nil
}

// getName returns a name of concrete engine implementation
func (e *TarantoolEngine) name() string {
	return "Tarantool"
}

// publish allows to send message into channel
func (e *TarantoolEngine) publish(chID ChannelID, message []byte) error {
	/*
		message:
			action: mark, push
			params:	[id,...]
	*/

	// Process service messages
	if chID != e.app.config.ControlChannel && chID != e.app.config.AdminChannel {
		if further, err := e.processMessage(chID, message); !further {
			return err // if no need further processing
		}
	}
	// All other messages
	return e.app.handleMsg(chID, message)
}

// subscribe on channel
func (e *TarantoolEngine) subscribe(chID ChannelID) (err error) {
	uid, ringno, project, err := parseChannelID(chID)
	if err != nil {
		return
	}

	conn, err := e.pool.get()
	if err != nil {
		logger.ERROR.Printf("subscribe tarantool pool error: %v\n", err.Error())
		return
	}

	_, err = conn.Call("notification_subscribe", []interface{}{uid, ringno, e.endpoint + "/api/" + project})

	return
}

// unsubscribe from channel
func (e *TarantoolEngine) unsubscribe(chID ChannelID) (err error) {
	uid, ringno, project, err := parseChannelID(chID)
	if err != nil {
		return
	}

	conn, err := e.pool.get()
	if err != nil {
		logger.ERROR.Printf("unsubscribe tarantool pool error: %v\n", err.Error())
		return
	}

	_, err = conn.Call("notification_unsubscribe", []interface{}{uid, ringno, e.endpoint + "/api/" + project})

	return
}

// addPresence sets or updates presence info for connection with uid
func (e *TarantoolEngine) addPresence(chID ChannelID, uid ConnID, info ClientInfo) (err error) {
	// not implemented
	return
}

// removePresence removes presence information for connection with uid
func (e *TarantoolEngine) removePresence(chID ChannelID, uid ConnID) (err error) {
	// not implemented
	return
}

// getPresence returns actual presence information for channel
func (e *TarantoolEngine) presence(chID ChannelID) (result map[ConnID]ClientInfo, err error) {
	// not implemented
	return
}

// addHistory adds message into channel history and takes care about history size
func (e *TarantoolEngine) addHistory(chID ChannelID, message Message, size, lifetime int64) (err error) {
	// not implemented
	return
}

// getHistory returns history messages for channel
// return empty slice
// all history pushed via publish
func (e *TarantoolEngine) history(chID ChannelID) (msgs []Message, err error) {
	uid, ringno, _, err := parseChannelID(chID)
	if err != nil {
		logger.ERROR.Printf("history parse chID error: %v\n", err.Error())
		return nil, err
	}

	conn, err := e.pool.get()
	if err != nil {
		logger.ERROR.Printf("history tarantool pool error: %v\n", err.Error())
		return nil, err
	}

	history, err := conn.Call("notification_read", []interface{}{uid, ringno})
	if err != nil {
		logger.ERROR.Printf("history notification_read error: %v\n", err.Error())
		return nil, err
	}

	return processHistory(history)
}

// helpers

type tarantoolHistoryItem struct {
	Count  interface{} `json:count`
	Status string      `json:status`
	ID     string      `json:id`
}

func processHistory(history *tarantool.Response) (msgs []Message, err error) {
	if len(history.Data) == 0 {
		return // history is empty
	}

	data := history.Data[0].([]interface{})
	if len(data) != 2 {
		return // history is empty
	}

	count := data[0]						// ring counter
	buffer := data[1].(string)				// string buffer
	ring := strings.Split(buffer[1:], ",")	// array of IDs

	if len(ring) == 0 {
		return	// history buffer is empty [useless?]
	}

	for _, id := range ring {
		encoded, err := json.Marshal(tarantoolHistoryItem{
			Count:  count, // redundancy in each item to pass number of unread notificatins
			Status: string(id[0]),
			ID:     string(id[1:]),
		})
		if err != nil {
			logger.ERROR.Println(err)
			continue
		}
		rawMessage := json.RawMessage([]byte(encoded))
		msgs = append(msgs, Message{Data: &rawMessage})
	}

	return
}

func (e *TarantoolEngine) processMessage(chID ChannelID, message []byte) (needFurtherProcessing bool, err error) {
	var msg MessageType
	err = json.Unmarshal(message, &msg)
	if err != nil {
		return true, err
	}

	var srv ServiceMessage
	err = json.Unmarshal(*msg.Body.Data, &srv)
	if err != nil {
		return true, err
	}

	if srv.Action == "" {
		return true, nil
	}

	var functionName string
	switch srv.Action {
	case "mark":
		functionName = "notification_mark"
	case "push":
		functionName = "notification_push"
	default:
		return true, nil
	}

	var uid, ringno int64
	uid, ringno, _, err = parseChannelID(chID)
	if err != nil {
		return
	}

	var conn *tarantool.Connection
	conn, err = e.pool.get()
	if err != nil {
		return
	}

	for _, id := range srv.Data {
		_, err = conn.Call(functionName, []interface{}{uid, ringno, id})
		if err != nil {
			logger.ERROR.Printf("%s call error: %s", functionName, err)
			return
		}
	}

	return
}

func parseChannelID(chID ChannelID) (uid, ringno int64, project string, err error) {
	// split chID <centrifugo>.<project>.[$]<uid>_<ringno>
	str := string(chID)
	logger.DEBUG.Printf("parseChannelID %s", str)

	result := strings.Split(str, ".")
	if len(result) != 3 {
		logger.DEBUG.Printf("unexpected ChannelID %s", str)
		return
	}

	project = result[1]
	str = result[2]

	separator := "_"
	prefix := "$"

	if strings.HasPrefix(str, prefix) {
		str = strings.TrimLeft(str, prefix)
	}
	channel := strings.Split(str, separator)

	uid, err = strconv.ParseInt(channel[0], 10, 64)
	if err != nil {
		return
	}
	ringno, err = strconv.ParseInt(channel[1], 10, 64)
	if err != nil {
		return
	}
	return
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/rand"
	"encoding/base64"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"os"
	"strings"

	"github.com/LiberisLabs/github-webhooks/github"
	"github.com/LiberisLabs/github-webhooks/handlers"
)

func randomCode(length int) string {
	b := make([]byte, length)
	rand.Read(b)

	return base64.StdEncoding.EncodeToString(b)
}

type oauthHandler struct {
	clientID     string
	clientSecret string
	redirectURL  string
	onSuccess    func(accessToken string) http.Handler

	handler http.Handler
}

func (h *oauthHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if h.handler != nil {
		h.handler.ServeHTTP(w, r)
		return
	}

	query := url.Values{}
	query.Add("client_id", h.clientID)
	query.Add("redirect_uri", h.redirectURL+"/oauth/callback")

	switch r.URL.Path {
	case "/oauth":
		stateCookie, err := r.Cookie("state")
		if err != nil || stateCookie == nil {
			http.Redirect(w, r, "/", http.StatusFound)
			return
		}

		redirectURL, _ := url.Parse("https://github.com/login/oauth/authorize")

		query.Add("state", stateCookie.Value)
		query.Add("scope", "repo read:repo_hook write:repo_hook")
		query.Add("allow_signup", "false")

		redirectURL.RawQuery = query.Encode()

		http.Redirect(w, r, redirectURL.String(), http.StatusFound)

	case "/oauth/callback":
		stateCookie, err := r.Cookie("state")
		if err != nil || stateCookie == nil || stateCookie.Value != r.FormValue("state") {
			http.Redirect(w, r, "/setup", http.StatusFound)
			return
		}

		tokenURL := "https://github.com/login/oauth/access_token"
		code := r.FormValue("code")

		query.Add("state", stateCookie.Value)
		query.Add("client_secret", h.clientSecret)
		query.Add("code", code)

		resp, err := http.PostForm(tokenURL, query)
		if err != nil {
			fmt.Fprint(w, "PostForm:", err)
			return
		}

		body, _ := ioutil.ReadAll(resp.Body)
		form, _ := url.ParseQuery(string(body))

		h.handler = h.onSuccess(form.Get("access_token"))
		http.Redirect(w, r, "/", http.StatusFound)

	default:
		http.SetCookie(w, &http.Cookie{
			Name:     "state",
			Value:    randomCode(10),
			HttpOnly: true,
		})

		http.Redirect(w, r, "/oauth", http.StatusFound)
	}
}

func mustGetenv(key string) string {
	value := os.Getenv(key)
	if value == "" {
		log.Fatalf("Require environment variable %q to be set", key)
	}
	return value
}

func installWebhook(c *github.Client, webhookURL, organization, secret string, logger *log.Logger) error {
	repos, err := c.ListRepositories(organization)
	if err != nil {
		return err
	}

	for _, repo := range repos {
		hooks, _ := c.GetHooks(repo.HooksURL)
		alreadyInstalled := false

		for _, hook := range hooks {
			if hook.Config.URL == webhookURL {
				alreadyInstalled = true
				break
			}
		}

		if !alreadyInstalled {
			logger.Printf("Installing webhook to %s", repo.FullName)

			c.CreateHook(repo.HooksURL, github.Hook{
				Active: true,
				Name:   "web",
				Events: []string{"issues"},
				Config: github.HookConfig{
					URL:         webhookURL,
					ContentType: "json",
					Secret:      secret,
				},
			})
		} else {
			logger.Printf("Webhook already installed to %s", repo.FullName)
		}
	}

	return nil
}

func main() {
	port := os.Getenv("PORT")
	secret := os.Getenv("GITHUB_WEBHOOK_SECRET")

	storyRepo := mustGetenv("STORY_REPO")
	oauthClientID := mustGetenv("OAUTH_CLIENT_ID")
	oauthClientSecret := mustGetenv("OAUTH_CLIENT_SECRET")
	oauthRedirectURL := mustGetenv("OAUTH_REDIRECT_URL")

	if port == "" {
		port = "8080"
	}

	log.Println("Listening on :" + port)

	http.ListenAndServe(":"+port, &oauthHandler{
		clientID:     oauthClientID,
		clientSecret: oauthClientSecret,
		redirectURL:  oauthRedirectURL,
		onSuccess: func(accessToken string) http.Handler {
			gitHubClient := &github.Client{
				Client:    http.DefaultClient,
				Token:     accessToken,
				UserAgent: "github.com/LiberisLabs/github-webhooks golang",
			}

			logger := log.New(os.Stdout, "", log.LstdFlags)

			go installWebhook(gitHubClient, oauthRedirectURL, strings.SplitN(storyRepo, "/", 2)[0], secret, logger)

			return &handlers.Handler{
				GitHubClient: gitHubClient,
				Logger:       logger,
				StoryRepo:    storyRepo,
				Secret:       []byte(secret),
			}
		},
	})
}
<commit_msg>Read the azure port<commit_after>package main

import (
	"crypto/rand"
	"encoding/base64"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"os"
	"strings"

	"github.com/LiberisLabs/github-webhooks/github"
	"github.com/LiberisLabs/github-webhooks/handlers"
)

func randomCode(length int) string {
	b := make([]byte, length)
	rand.Read(b)

	return base64.StdEncoding.EncodeToString(b)
}

type oauthHandler struct {
	clientID     string
	clientSecret string
	redirectURL  string
	onSuccess    func(accessToken string) http.Handler

	handler http.Handler
}

func (h *oauthHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if h.handler != nil {
		h.handler.ServeHTTP(w, r)
		return
	}

	query := url.Values{}
	query.Add("client_id", h.clientID)
	query.Add("redirect_uri", h.redirectURL+"/oauth/callback")

	switch r.URL.Path {
	case "/oauth":
		stateCookie, err := r.Cookie("state")
		if err != nil || stateCookie == nil {
			http.Redirect(w, r, "/", http.StatusFound)
			return
		}

		redirectURL, _ := url.Parse("https://github.com/login/oauth/authorize")

		query.Add("state", stateCookie.Value)
		query.Add("scope", "repo read:repo_hook write:repo_hook")
		query.Add("allow_signup", "false")

		redirectURL.RawQuery = query.Encode()

		http.Redirect(w, r, redirectURL.String(), http.StatusFound)

	case "/oauth/callback":
		stateCookie, err := r.Cookie("state")
		if err != nil || stateCookie == nil || stateCookie.Value != r.FormValue("state") {
			http.Redirect(w, r, "/setup", http.StatusFound)
			return
		}

		tokenURL := "https://github.com/login/oauth/access_token"
		code := r.FormValue("code")

		query.Add("state", stateCookie.Value)
		query.Add("client_secret", h.clientSecret)
		query.Add("code", code)

		resp, err := http.PostForm(tokenURL, query)
		if err != nil {
			fmt.Fprint(w, "PostForm:", err)
			return
		}

		body, _ := ioutil.ReadAll(resp.Body)
		form, _ := url.ParseQuery(string(body))

		h.handler = h.onSuccess(form.Get("access_token"))
		http.Redirect(w, r, "/", http.StatusFound)

	default:
		http.SetCookie(w, &http.Cookie{
			Name:     "state",
			Value:    randomCode(10),
			HttpOnly: true,
		})

		http.Redirect(w, r, "/oauth", http.StatusFound)
	}
}

func mustGetenv(key string) string {
	value := os.Getenv(key)
	if value == "" {
		log.Fatalf("Require environment variable %q to be set", key)
	}
	return value
}

func installWebhook(c *github.Client, webhookURL, organization, secret string, logger *log.Logger) error {
	repos, err := c.ListRepositories(organization)
	if err != nil {
		return err
	}

	for _, repo := range repos {
		hooks, _ := c.GetHooks(repo.HooksURL)
		alreadyInstalled := false

		for _, hook := range hooks {
			if hook.Config.URL == webhookURL {
				alreadyInstalled = true
				break
			}
		}

		if !alreadyInstalled {
			logger.Printf("Installing webhook to %s", repo.FullName)

			c.CreateHook(repo.HooksURL, github.Hook{
				Active: true,
				Name:   "web",
				Events: []string{"issues"},
				Config: github.HookConfig{
					URL:         webhookURL,
					ContentType: "json",
					Secret:      secret,
				},
			})
		} else {
			logger.Printf("Webhook already installed to %s", repo.FullName)
		}
	}

	return nil
}

func main() {
	port := os.Getenv("PORT")
	secret := os.Getenv("GITHUB_WEBHOOK_SECRET")

	storyRepo := mustGetenv("STORY_REPO")
	oauthClientID := mustGetenv("OAUTH_CLIENT_ID")
	oauthClientSecret := mustGetenv("OAUTH_CLIENT_SECRET")
	oauthRedirectURL := mustGetenv("OAUTH_REDIRECT_URL")

	if port == "" {
		port = os.Getenv("HTTP_PLATFORM_PORT")
	}
	if port == "" {
		port = "8080"
	}

	log.Println("Listening on :" + port)

	http.ListenAndServe(":"+port, &oauthHandler{
		clientID:     oauthClientID,
		clientSecret: oauthClientSecret,
		redirectURL:  oauthRedirectURL,
		onSuccess: func(accessToken string) http.Handler {
			gitHubClient := &github.Client{
				Client:    http.DefaultClient,
				Token:     accessToken,
				UserAgent: "github.com/LiberisLabs/github-webhooks golang",
			}

			logger := log.New(os.Stdout, "", log.LstdFlags)

			go installWebhook(gitHubClient, oauthRedirectURL, strings.SplitN(storyRepo, "/", 2)[0], secret, logger)

			return &handlers.Handler{
				GitHubClient: gitHubClient,
				Logger:       logger,
				StoryRepo:    storyRepo,
				Secret:       []byte(secret),
			}
		},
	})
}
<|endoftext|>


<commit_before>package loadbalance

import (
	"sync"

	"github.com/zouyx/agollo/v3/env/config"
)

var defaultLoadBalance LoadBalance

//LoadBalance 负载均衡器
type LoadBalance interface {
	//Load 负载均衡，获取对应服务信息
	Load(servers *sync.Map) *config.ServerInfo
}

//SetLoadBalance 设置负载均衡器
func SetLoadBalance(loadBalance LoadBalance) {
	defaultLoadBalance = loadBalance
}

//GetLoadBalance 获取负载均衡器
func GetLoadBalance() LoadBalance {
	return defaultLoadBalance
}
<commit_msg>add test case<commit_after>package loadbalance

import (
	"sync"
	"testing"

	. "github.com/tevid/gohamcrest"
	"github.com/zouyx/agollo/v3/env/config"
)

type TestLoadBalance struct {
}

//Load 负载均衡
func (r *TestLoadBalance) Load(servers *sync.Map) *config.ServerInfo {
	return nil
}

func TestSetLoadBalance(t *testing.T) {
	SetLoadBalance(&TestLoadBalance{})

	balance := GetLoadBalance()

	b := balance.(*TestLoadBalance)
	Assert(t, b, NotNilVal())
}
<|endoftext|>


<commit_before>/*
Copyright 2015 The Kubernetes 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 e2e

import (
	"fmt"
	"path/filepath"
	"time"

	"k8s.io/kubernetes/test/e2e/framework"

	. "github.com/onsi/ginkgo"
)

const (
	// parent path to yaml test manifests.
	ingressManifestPath = "test/e2e/testing-manifests/ingress"

	// timeout on a single http request.
	reqTimeout = 10 * time.Second

	// healthz port used to verify glbc restarted correctly on the master.
	glbcHealthzPort = 8086

	// On average it takes ~6 minutes for a single backend to come online in GCE.
	lbPollTimeout = 15 * time.Minute

	// General cloud resource poll timeout (eg: create static ip, firewall etc)
	cloudResourcePollTimeout = 5 * time.Minute

	// Time required by the loadbalancer to cleanup, proportional to numApps/Ing.
	// Bring the cleanup timeout back down to 5m once b/33588344 is resolved.
	lbCleanupTimeout = 15 * time.Minute
	lbPollInterval   = 30 * time.Second

	// Name of the config-map and key the ingress controller stores its uid in.
	uidConfigMap = "ingress-uid"
	uidKey       = "uid"

	// GCE only allows names < 64 characters, and the loadbalancer controller inserts
	// a single character of padding.
	nameLenLimit = 62
)

var _ = framework.KubeDescribe("Loadbalancing: L7", func() {
	defer GinkgoRecover()
	var (
		ns               string
		jig              *testJig
		conformanceTests []conformanceTests
	)
	f := framework.NewDefaultFramework("ingress")

	BeforeEach(func() {
		f.BeforeEach()
		jig = newTestJig(f.ClientSet)
		ns = f.Namespace.Name
	})

	// Before enabling this loadbalancer test in any other test list you must
	// make sure the associated project has enough quota. At the time of this
	// writing a GCE project is allowed 3 backend services by default. This
	// test requires at least 5.
	//
	// Slow by design ~10m for each "It" block dominated by loadbalancer setup time
	// TODO: write similar tests for nginx, haproxy and AWS Ingress.
	framework.KubeDescribe("GCE [Slow] [Feature: Ingress]", func() {
		var gceController *GCEIngressController

		// Platform specific setup
		BeforeEach(func() {
			framework.SkipUnlessProviderIs("gce", "gke")
			By("Initializing gce controller")
			gceController = &GCEIngressController{
				ns:    ns,
				c:     jig.client,
				cloud: framework.TestContext.CloudConfig,
			}
			gceController.init()
		})

		// Platform specific cleanup
		AfterEach(func() {
			if CurrentGinkgoTestDescription().Failed {
				describeIng(ns)
			}
			if jig.ing == nil {
				By("No ingress created, no cleanup necessary")
				return
			}
			By("Deleting ingress")
			jig.deleteIngress()

			By("Cleaning up cloud resources")
			cleanupGCE(gceController)
		})

		It("should conform to Ingress spec", func() {
			conformanceTests = createComformanceTests(jig, ns)
			for _, t := range conformanceTests {
				By(t.entryLog)
				t.execute()
				By(t.exitLog)
				jig.waitForIngress()
			}
		})

		It("shoud create ingress with given static-ip ", func() {
			// ip released when the rest of lb resources are deleted in cleanupGCE
			ip := gceController.createStaticIP(ns)
			By(fmt.Sprintf("allocated static ip %v: %v through the GCE cloud provider", ns, ip))

			jig.createIngress(filepath.Join(ingressManifestPath, "static-ip"), ns, map[string]string{
				"kubernetes.io/ingress.global-static-ip-name": ns,
				"kubernetes.io/ingress.allow-http":            "false",
			})

			By("waiting for Ingress to come up with ip: " + ip)
			httpClient := buildInsecureClient(reqTimeout)
			framework.ExpectNoError(pollURL(fmt.Sprintf("https://%v/", ip), "", lbPollTimeout, jig.pollInterval, httpClient, false))

			By("should reject HTTP traffic")
			framework.ExpectNoError(pollURL(fmt.Sprintf("http://%v/", ip), "", lbPollTimeout, jig.pollInterval, httpClient, true))

			// TODO: uncomment the restart test once we have a way to synchronize
			// and know that the controller has resumed watching. If we delete
			// the ingress before the controller is ready we will leak.
			// By("restaring glbc")
			// restarter := NewRestartConfig(
			//	 framework.GetMasterHost(), "glbc", glbcHealthzPort, restartPollInterval, restartTimeout)
			// restarter.restart()
			// By("should continue serving on provided static-ip for 30 seconds")
			// ExpectNoError(jig.verifyURL(fmt.Sprintf("https://%v/", ip), "", 30, 1*time.Second, httpClient))
		})

		// TODO: Implement a multizone e2e that verifies traffic reaches each
		// zone based on pod labels.
	})

	// Time: borderline 5m, slow by design
	framework.KubeDescribe("Nginx", func() {
		var nginxController *NginxIngressController

		BeforeEach(func() {
			framework.SkipUnlessProviderIs("gce", "gke")
			By("Initializing nginx controller")
			jig.class = "nginx"
			nginxController = &NginxIngressController{ns: ns, c: jig.client}

			// TODO: This test may fail on other platforms. We can simply skip it
			// but we want to allow easy testing where a user might've hand
			// configured firewalls.
			if framework.ProviderIs("gce", "gke") {
				framework.ExpectNoError(gcloudCreate("firewall-rules", fmt.Sprintf("ingress-80-443-%v", ns), framework.TestContext.CloudConfig.ProjectID, "--allow", "tcp:80,tcp:443", "--network", framework.TestContext.CloudConfig.Network))
			} else {
				framework.Logf("WARNING: Not running on GCE/GKE, cannot create firewall rules for :80, :443. Assuming traffic can reach the external ips of all nodes in cluster on those ports.")
			}

			nginxController.init()
		})

		AfterEach(func() {
			if framework.ProviderIs("gce", "gke") {
				framework.ExpectNoError(gcloudDelete("firewall-rules", fmt.Sprintf("ingress-80-443-%v", ns), framework.TestContext.CloudConfig.ProjectID))
			}
			if CurrentGinkgoTestDescription().Failed {
				describeIng(ns)
			}
			if jig.ing == nil {
				By("No ingress created, no cleanup necessary")
				return
			}
			By("Deleting ingress")
			jig.deleteIngress()
		})

		It("should conform to Ingress spec", func() {
			// Poll more frequently to reduce e2e completion time.
			// This test runs in presubmit.
			jig.pollInterval = 5 * time.Second
			conformanceTests = createComformanceTests(jig, ns)
			for _, t := range conformanceTests {
				By(t.entryLog)
				t.execute()
				By(t.exitLog)
				jig.waitForIngress()
			}
		})
	})
})
<commit_msg>Remove a space in ingress e2e title<commit_after>/*
Copyright 2015 The Kubernetes 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 e2e

import (
	"fmt"
	"path/filepath"
	"time"

	"k8s.io/kubernetes/test/e2e/framework"

	. "github.com/onsi/ginkgo"
)

const (
	// parent path to yaml test manifests.
	ingressManifestPath = "test/e2e/testing-manifests/ingress"

	// timeout on a single http request.
	reqTimeout = 10 * time.Second

	// healthz port used to verify glbc restarted correctly on the master.
	glbcHealthzPort = 8086

	// On average it takes ~6 minutes for a single backend to come online in GCE.
	lbPollTimeout = 15 * time.Minute

	// General cloud resource poll timeout (eg: create static ip, firewall etc)
	cloudResourcePollTimeout = 5 * time.Minute

	// Time required by the loadbalancer to cleanup, proportional to numApps/Ing.
	// Bring the cleanup timeout back down to 5m once b/33588344 is resolved.
	lbCleanupTimeout = 15 * time.Minute
	lbPollInterval   = 30 * time.Second

	// Name of the config-map and key the ingress controller stores its uid in.
	uidConfigMap = "ingress-uid"
	uidKey       = "uid"

	// GCE only allows names < 64 characters, and the loadbalancer controller inserts
	// a single character of padding.
	nameLenLimit = 62
)

var _ = framework.KubeDescribe("Loadbalancing: L7", func() {
	defer GinkgoRecover()
	var (
		ns               string
		jig              *testJig
		conformanceTests []conformanceTests
	)
	f := framework.NewDefaultFramework("ingress")

	BeforeEach(func() {
		f.BeforeEach()
		jig = newTestJig(f.ClientSet)
		ns = f.Namespace.Name
	})

	// Before enabling this loadbalancer test in any other test list you must
	// make sure the associated project has enough quota. At the time of this
	// writing a GCE project is allowed 3 backend services by default. This
	// test requires at least 5.
	//
	// Slow by design ~10m for each "It" block dominated by loadbalancer setup time
	// TODO: write similar tests for nginx, haproxy and AWS Ingress.
	framework.KubeDescribe("GCE [Slow] [Feature:Ingress]", func() {
		var gceController *GCEIngressController

		// Platform specific setup
		BeforeEach(func() {
			framework.SkipUnlessProviderIs("gce", "gke")
			By("Initializing gce controller")
			gceController = &GCEIngressController{
				ns:    ns,
				c:     jig.client,
				cloud: framework.TestContext.CloudConfig,
			}
			gceController.init()
		})

		// Platform specific cleanup
		AfterEach(func() {
			if CurrentGinkgoTestDescription().Failed {
				describeIng(ns)
			}
			if jig.ing == nil {
				By("No ingress created, no cleanup necessary")
				return
			}
			By("Deleting ingress")
			jig.deleteIngress()

			By("Cleaning up cloud resources")
			cleanupGCE(gceController)
		})

		It("should conform to Ingress spec", func() {
			conformanceTests = createComformanceTests(jig, ns)
			for _, t := range conformanceTests {
				By(t.entryLog)
				t.execute()
				By(t.exitLog)
				jig.waitForIngress()
			}
		})

		It("shoud create ingress with given static-ip ", func() {
			// ip released when the rest of lb resources are deleted in cleanupGCE
			ip := gceController.createStaticIP(ns)
			By(fmt.Sprintf("allocated static ip %v: %v through the GCE cloud provider", ns, ip))

			jig.createIngress(filepath.Join(ingressManifestPath, "static-ip"), ns, map[string]string{
				"kubernetes.io/ingress.global-static-ip-name": ns,
				"kubernetes.io/ingress.allow-http":            "false",
			})

			By("waiting for Ingress to come up with ip: " + ip)
			httpClient := buildInsecureClient(reqTimeout)
			framework.ExpectNoError(pollURL(fmt.Sprintf("https://%v/", ip), "", lbPollTimeout, jig.pollInterval, httpClient, false))

			By("should reject HTTP traffic")
			framework.ExpectNoError(pollURL(fmt.Sprintf("http://%v/", ip), "", lbPollTimeout, jig.pollInterval, httpClient, true))

			// TODO: uncomment the restart test once we have a way to synchronize
			// and know that the controller has resumed watching. If we delete
			// the ingress before the controller is ready we will leak.
			// By("restaring glbc")
			// restarter := NewRestartConfig(
			//	 framework.GetMasterHost(), "glbc", glbcHealthzPort, restartPollInterval, restartTimeout)
			// restarter.restart()
			// By("should continue serving on provided static-ip for 30 seconds")
			// ExpectNoError(jig.verifyURL(fmt.Sprintf("https://%v/", ip), "", 30, 1*time.Second, httpClient))
		})

		// TODO: Implement a multizone e2e that verifies traffic reaches each
		// zone based on pod labels.
	})

	// Time: borderline 5m, slow by design
	framework.KubeDescribe("Nginx", func() {
		var nginxController *NginxIngressController

		BeforeEach(func() {
			framework.SkipUnlessProviderIs("gce", "gke")
			By("Initializing nginx controller")
			jig.class = "nginx"
			nginxController = &NginxIngressController{ns: ns, c: jig.client}

			// TODO: This test may fail on other platforms. We can simply skip it
			// but we want to allow easy testing where a user might've hand
			// configured firewalls.
			if framework.ProviderIs("gce", "gke") {
				framework.ExpectNoError(gcloudCreate("firewall-rules", fmt.Sprintf("ingress-80-443-%v", ns), framework.TestContext.CloudConfig.ProjectID, "--allow", "tcp:80,tcp:443", "--network", framework.TestContext.CloudConfig.Network))
			} else {
				framework.Logf("WARNING: Not running on GCE/GKE, cannot create firewall rules for :80, :443. Assuming traffic can reach the external ips of all nodes in cluster on those ports.")
			}

			nginxController.init()
		})

		AfterEach(func() {
			if framework.ProviderIs("gce", "gke") {
				framework.ExpectNoError(gcloudDelete("firewall-rules", fmt.Sprintf("ingress-80-443-%v", ns), framework.TestContext.CloudConfig.ProjectID))
			}
			if CurrentGinkgoTestDescription().Failed {
				describeIng(ns)
			}
			if jig.ing == nil {
				By("No ingress created, no cleanup necessary")
				return
			}
			By("Deleting ingress")
			jig.deleteIngress()
		})

		It("should conform to Ingress spec", func() {
			// Poll more frequently to reduce e2e completion time.
			// This test runs in presubmit.
			jig.pollInterval = 5 * time.Second
			conformanceTests = createComformanceTests(jig, ns)
			for _, t := range conformanceTests {
				By(t.entryLog)
				t.execute()
				By(t.exitLog)
				jig.waitForIngress()
			}
		})
	})
})
<|endoftext|>


<commit_before>/*
Copyright 2015 Google Inc. All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package e2e

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"os/exec"
	"path/filepath"
	"strings"
	"time"

	. "github.com/onsi/ginkgo"
)

const (
	nautilusImage       = "kubernetes/update-demo:nautilus"
	kittenImage         = "kubernetes/update-demo:kitten"
	updateDemoSelector  = "name=update-demo"
	updateDemoContainer = "update-demo"
)

var _ = Describe("kubectl", func() {

	updateDemoRoot := filepath.Join(root, "examples/update-demo")
	nautilusPath := filepath.Join(updateDemoRoot, "nautilus-rc.yaml")
	kittenPath := filepath.Join(updateDemoRoot, "kitten-rc.yaml")

	It("should create and stop a replication controller", func() {
		defer cleanup(nautilusPath)

		By("creating a replication controller")
		runKubectl("create", "-f", nautilusPath)
		validateController(nautilusImage, 2, 30*time.Second)
	})

	It("should scale a replication controller", func() {
		defer cleanup(nautilusPath)

		By("creating a replication controller")
		runKubectl("create", "-f", nautilusPath)
		validateController(nautilusImage, 2, 30*time.Second)
		By("scaling down the replication controller")
		runKubectl("resize", "rc", "update-demo-nautilus", "--replicas=1")
		validateController(nautilusImage, 1, 30*time.Second)
		By("scaling up the replication controller")
		runKubectl("resize", "rc", "update-demo-nautilus", "--replicas=2")
		validateController(nautilusImage, 2, 30*time.Second)
	})

	It("should do a rolling update of a replication controller", func() {
		// Cleanup all resources in case we fail somewhere in the middle
		defer cleanup(updateDemoRoot)

		By("creating the initial replication controller")
		runKubectl("create", "-f", nautilusPath)
		validateController(nautilusImage, 2, 30*time.Second)
		By("rollingupdate to new replication controller")
		runKubectl("rollingupdate", "update-demo-nautilus", "--update-period=1s", "-f", kittenPath)
		validateController(kittenImage, 2, 30*time.Second)
	})

})

func cleanup(filePath string) {
	By("using stop to clean up resources")
	runKubectl("stop", "-f", filePath)

	resources := runKubectl("get", "pods,rc", "-l", updateDemoSelector, "--no-headers")
	if resources != "" {
		Failf("Resources left running after stop:\n%s", resources)
	}
}

func validateController(image string, replicas int, timeout time.Duration) {

	getPodsTemplate := "--template={{range.items}}{{.id}} {{end}}"

	// NB: kubectl adds the "exists" function to the standard template functions.
	// This lets us check to see if the "running" entry exists for each of the containers
	// we care about. Exists will never return an error and it's safe to check a chain of
	// things, any one of which may not exist. In the below template, all of info,
	// containername, and running might be nil, so the normal index function isn't very
	// helpful.
	// This template is unit-tested in kubectl, so if you change it, update the unit test.
	//
	// You can read about the syntax here: http://golang.org/pkg/text/template/
	getContainerStateTemplate := fmt.Sprintf(`--template={{and (exists . "currentState" "info" "%s" "state" "running")}}`, updateDemoContainer)

	getImageTemplate := fmt.Sprintf(`--template={{(index .currentState.info "%s").image}}`, updateDemoContainer)

	getHostIPTemplate := "--template={{.currentState.hostIP}}"

	By(fmt.Sprintf("waiting for all containers in %s pods to come up.", updateDemoSelector))
	for start := time.Now(); time.Since(start) < timeout; time.Sleep(5 * time.Second) {
		getPodsOutput := runKubectl("get", "pods", "-o", "template", getPodsTemplate, "-l", updateDemoSelector)
		pods := strings.Fields(getPodsOutput)
		if numPods := len(pods); numPods != replicas {
			By(fmt.Sprintf("Replicas for %s: expected=%d actual=%d", updateDemoSelector, replicas, numPods))
			continue
		}
		var runningPods []string
		for _, podId := range pods {
			running := runKubectl("get", "pods", podId, "-o", "template", getContainerStateTemplate)
			if running == "false" {
				By(fmt.Sprintf("%s is created but not running", podId))
				continue
			}

			currentImage := runKubectl("get", "pods", podId, "-o", "template", getImageTemplate)
			if currentImage != image {
				By(fmt.Sprintf("%s is created but running wrong image; expected: %s, actual: %s", podId, image, currentImage))
				continue
			}

			hostIP := runKubectl("get", "pods", podId, "-o", "template", getHostIPTemplate)
			data, err := getData(hostIP)
			if err != nil {
				By(fmt.Sprintf("%s is running right image but fetching data failed: %v", podId, err))
				continue
			}
			if strings.Contains(data.image, image) {
				By(fmt.Sprintf("%s is running right image but fetched data has the wrong info: %s", podId, data))
				continue
			}

			Logf("%s is verified up and running", podId)
			runningPods = append(runningPods, podId)
		}
		if len(runningPods) == replicas {
			return
		}
	}
	Failf("Timed out waiting for %s pods to reach valid state", updateDemoSelector)
}

type updateDemoData struct {
	image string `json:"image"`
}

func getData(hostIP string) (*updateDemoData, error) {
	addr := fmt.Sprintf("http://%s:8080/data.json", hostIP)
	resp, err := http.Get(fmt.Sprintf(addr))
	if err != nil || resp.StatusCode != 200 {
		return nil, err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	Logf("got data: %s", body)
	var data updateDemoData
	err = json.Unmarshal(body, &data)
	return &data, err
}

func runKubectl(args ...string) string {
	// TODO: use kubectl binary directly instead of shell wrapper
	path := filepath.Join(root, "cluster/kubectl.sh")
	cmdStr := path + " " + strings.Join(args, " ")
	Logf("Running '%v'", cmdStr)

	cmd := exec.Command(path, args...)
	var stdout, stderr bytes.Buffer
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr

	if err := cmd.Run(); err != nil {
		Failf("Error running %v:\nCommand stdout:\n%v\nstderr:\n%v\n", cmd, cmd.Stdout, cmd.Stderr)
		return ""
	}
	Logf(stdout.String())
	// TODO: trimspace should be unnecessary after switching to use kubectl binary directly
	return strings.TrimSpace(stdout.String())
}
<commit_msg>Increase timeout on kubectl.go to 60s<commit_after>/*
Copyright 2015 Google Inc. All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package e2e

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"os/exec"
	"path/filepath"
	"strings"
	"time"

	. "github.com/onsi/ginkgo"
)

const (
	nautilusImage       = "kubernetes/update-demo:nautilus"
	kittenImage         = "kubernetes/update-demo:kitten"
	updateDemoSelector  = "name=update-demo"
	updateDemoContainer = "update-demo"
	validateTimeout     = 60 * time.Second
)

var _ = Describe("kubectl", func() {

	updateDemoRoot := filepath.Join(root, "examples/update-demo")
	nautilusPath := filepath.Join(updateDemoRoot, "nautilus-rc.yaml")
	kittenPath := filepath.Join(updateDemoRoot, "kitten-rc.yaml")

	It("should create and stop a replication controller", func() {
		defer cleanup(nautilusPath)

		By("creating a replication controller")
		runKubectl("create", "-f", nautilusPath)
		validateController(nautilusImage, 2)
	})

	It("should scale a replication controller", func() {
		defer cleanup(nautilusPath)

		By("creating a replication controller")
		runKubectl("create", "-f", nautilusPath)
		validateController(nautilusImage, 2)
		By("scaling down the replication controller")
		runKubectl("resize", "rc", "update-demo-nautilus", "--replicas=1")
		validateController(nautilusImage, 1)
		By("scaling up the replication controller")
		runKubectl("resize", "rc", "update-demo-nautilus", "--replicas=2")
		validateController(nautilusImage, 2)
	})

	It("should do a rolling update of a replication controller", func() {
		// Cleanup all resources in case we fail somewhere in the middle
		defer cleanup(updateDemoRoot)

		By("creating the initial replication controller")
		runKubectl("create", "-f", nautilusPath)
		validateController(nautilusImage, 2)
		By("rollingupdate to new replication controller")
		runKubectl("rollingupdate", "update-demo-nautilus", "--update-period=1s", "-f", kittenPath)
		validateController(kittenImage, 2)
	})

})

func cleanup(filePath string) {
	By("using stop to clean up resources")
	runKubectl("stop", "-f", filePath)

	resources := runKubectl("get", "pods,rc", "-l", updateDemoSelector, "--no-headers")
	if resources != "" {
		Failf("Resources left running after stop:\n%s", resources)
	}
}

func validateController(image string, replicas int) {

	getPodsTemplate := "--template={{range.items}}{{.id}} {{end}}"

	// NB: kubectl adds the "exists" function to the standard template functions.
	// This lets us check to see if the "running" entry exists for each of the containers
	// we care about. Exists will never return an error and it's safe to check a chain of
	// things, any one of which may not exist. In the below template, all of info,
	// containername, and running might be nil, so the normal index function isn't very
	// helpful.
	// This template is unit-tested in kubectl, so if you change it, update the unit test.
	//
	// You can read about the syntax here: http://golang.org/pkg/text/template/
	getContainerStateTemplate := fmt.Sprintf(`--template={{and (exists . "currentState" "info" "%s" "state" "running")}}`, updateDemoContainer)

	getImageTemplate := fmt.Sprintf(`--template={{(index .currentState.info "%s").image}}`, updateDemoContainer)

	getHostIPTemplate := "--template={{.currentState.hostIP}}"

	By(fmt.Sprintf("waiting for all containers in %s pods to come up.", updateDemoSelector))
	for start := time.Now(); time.Since(start) < validateTimeout; time.Sleep(5 * time.Second) {
		getPodsOutput := runKubectl("get", "pods", "-o", "template", getPodsTemplate, "-l", updateDemoSelector)
		pods := strings.Fields(getPodsOutput)
		if numPods := len(pods); numPods != replicas {
			By(fmt.Sprintf("Replicas for %s: expected=%d actual=%d", updateDemoSelector, replicas, numPods))
			continue
		}
		var runningPods []string
		for _, podId := range pods {
			running := runKubectl("get", "pods", podId, "-o", "template", getContainerStateTemplate)
			if running == "false" {
				By(fmt.Sprintf("%s is created but not running", podId))
				continue
			}

			currentImage := runKubectl("get", "pods", podId, "-o", "template", getImageTemplate)
			if currentImage != image {
				By(fmt.Sprintf("%s is created but running wrong image; expected: %s, actual: %s", podId, image, currentImage))
				continue
			}

			hostIP := runKubectl("get", "pods", podId, "-o", "template", getHostIPTemplate)
			data, err := getData(hostIP)
			if err != nil {
				By(fmt.Sprintf("%s is running right image but fetching data failed: %v", podId, err))
				continue
			}
			if strings.Contains(data.image, image) {
				By(fmt.Sprintf("%s is running right image but fetched data has the wrong info: %s", podId, data))
				continue
			}

			Logf("%s is verified up and running", podId)
			runningPods = append(runningPods, podId)
		}
		if len(runningPods) == replicas {
			return
		}
	}
	Failf("Timed out waiting for %s pods to reach valid state", updateDemoSelector)
}

type updateDemoData struct {
	image string `json:"image"`
}

func getData(hostIP string) (*updateDemoData, error) {
	addr := fmt.Sprintf("http://%s:8080/data.json", hostIP)
	resp, err := http.Get(fmt.Sprintf(addr))
	if err != nil || resp.StatusCode != 200 {
		return nil, err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	Logf("got data: %s", body)
	var data updateDemoData
	err = json.Unmarshal(body, &data)
	return &data, err
}

func runKubectl(args ...string) string {
	// TODO: use kubectl binary directly instead of shell wrapper
	path := filepath.Join(root, "cluster/kubectl.sh")
	cmdStr := path + " " + strings.Join(args, " ")
	Logf("Running '%v'", cmdStr)

	cmd := exec.Command(path, args...)
	var stdout, stderr bytes.Buffer
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr

	if err := cmd.Run(); err != nil {
		Failf("Error running %v:\nCommand stdout:\n%v\nstderr:\n%v\n", cmd, cmd.Stdout, cmd.Stderr)
		return ""
	}
	Logf(stdout.String())
	// TODO: trimspace should be unnecessary after switching to use kubectl binary directly
	return strings.TrimSpace(stdout.String())
}
<|endoftext|>


<commit_before>package apis

import (
	"crypto/md5"
	"fmt"
	"io"
	"mime/multipart"
	"os"
	"path"

	"github.com/tukdesk/tukdesk/backend/models"
)

type AttachmentStorager interface {
	Store(*multipart.FileHeader) (*models.Attachment, error)
}

type sizer interface {
	Size() int64
}

type stater interface {
	Stat() (os.FileInfo, error)
}

var ErrExpectingOSFile = fmt.Errorf("expected an *os.File")

const (
	defaultDirPerm  = 0755
	defaultFilePerm = 0666

	pathSeparater = "/"
)

type InternalLocalStorager struct {
	dir string
}

func newInternalLocalStorager(dir string) (AttachmentStorager, error) {
	if err := os.MkdirAll(dir, defaultDirPerm); err != nil {
		return nil, err
	}

	return &InternalLocalStorager{
		dir: dir,
	}, nil
}

func (this *InternalLocalStorager) Store(header *multipart.FileHeader) (*models.Attachment, error) {
	reader, err := header.Open()
	if err != nil {
		return nil, err
	}
	defer reader.Close()

	// 生成 attachment
	attachment := models.NewAttachment()
	attachment.IsInternal = true

	// 获取文件大小
	if rSizer, ok := reader.(sizer); ok {
		attachment.FileSize = rSizer.Size()
	} else if rStater, ok := reader.(stater); ok {
		stat, err := rStater.Stat()
		if err != nil {
			return nil, err
		}
		attachment.FileSize = stat.Size()
	}

	// 获取文件类型

	// 生成 sub dir
	fileKey := attachment.Id.Hex()
	subDir := generateSubDir([]byte(fileKey))
	dir := path.Join(this.dir, subDir)
	if err := os.MkdirAll(dir, defaultDirPerm); err != nil {
		return nil, err
	}

	// 完整文件名
	ext := path.Ext(header.Filename)
	if ext != "" {
		fileKey += ext
	}

	filePath := path.Join(dir, fileKey)

	// 存储到本地
	f, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE, defaultFilePerm)
	if err != nil {
		return nil, err
	}

	defer f.Close()
	if _, err := io.Copy(f, reader); err != nil {
		return nil, err
	}

	attachment.FileKey = path.Join(subDir, fileKey)

	return attachment, nil
}

func generateSubDir(fileKey []byte) string {
	b := md5.Sum(fileKey)
	s := fmt.Sprintf("%x", b)
	return s[0:3] + pathSeparater + s[3:6] + pathSeparater + s[6:9]
}
<commit_msg>mime type<commit_after>package apis

import (
	"crypto/md5"
	"fmt"
	"io"
	"mime/multipart"
	"net/http"
	"os"
	"path"

	"github.com/tukdesk/tukdesk/backend/models"
)

type AttachmentStorager interface {
	Store(*multipart.FileHeader) (*models.Attachment, error)
}

type sizer interface {
	Size() int64
}

type stater interface {
	Stat() (os.FileInfo, error)
}

var (
	ErrExpectingOSFile = fmt.Errorf("expected an *os.File")
)

const (
	defaultDirPerm  = 0755
	defaultFilePerm = 0666

	pathSeparater = "/"
)

type InternalLocalStorager struct {
	dir string
}

func newInternalLocalStorager(dir string) (AttachmentStorager, error) {
	if err := os.MkdirAll(dir, defaultDirPerm); err != nil {
		return nil, err
	}

	return &InternalLocalStorager{
		dir: dir,
	}, nil
}

func (this *InternalLocalStorager) Store(header *multipart.FileHeader) (*models.Attachment, error) {
	reader, err := header.Open()
	if err != nil {
		return nil, err
	}
	defer reader.Close()

	// 生成 attachment
	attachment := models.NewAttachment()
	attachment.IsInternal = true

	// 获取文件大小
	if rSizer, ok := reader.(sizer); ok {
		attachment.FileSize = rSizer.Size()
	} else if rStater, ok := reader.(stater); ok {
		stat, err := rStater.Stat()
		if err != nil {
			return nil, err
		}
		attachment.FileSize = stat.Size()
	}

	// 获取文件类型
	attachment.MimeType = detectFileType(reader)

	// 生成 sub dir
	fileKey := attachment.Id.Hex()
	subDir := generateSubDir([]byte(fileKey))
	dir := path.Join(this.dir, subDir)
	if err := os.MkdirAll(dir, defaultDirPerm); err != nil {
		return nil, err
	}

	// 完整文件名
	ext := path.Ext(header.Filename)
	if ext != "" {
		fileKey += ext
	}

	filePath := path.Join(dir, fileKey)

	// 存储到本地
	f, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE, defaultFilePerm)
	if err != nil {
		return nil, err
	}

	defer f.Close()
	if _, err := io.Copy(f, reader); err != nil {
		return nil, err
	}

	attachment.FileKey = path.Join(subDir, fileKey)

	return attachment, nil
}

func generateSubDir(fileKey []byte) string {
	b := md5.Sum(fileKey)
	s := fmt.Sprintf("%x", b)
	return s[0:3] + pathSeparater + s[3:6] + pathSeparater + s[6:9]
}

func detectFileType(reader multipart.File) string {
	head := make([]byte, 512)
	reader.Read(head)
	reader.Seek(0, 0)
	return http.DetectContentType(head)
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"testing"
	"time"
)

type testWriter struct {
	written [][]byte
	closed  bool
}

func (t *testWriter) Write(p []byte) (int, error) {
	b := make([]byte, len(p))
	copy(b, p)
	t.written = append(t.written, b)
	return len(p), nil
}

func (t *testWriter) Close() error {
	t.closed = true
	return nil
}

func TestCoalesceWriteCloser(t *testing.T) {
	tw := &testWriter{written: make([][]byte, 0)}
	cw := NewCoalesceWriteCloser(tw)

	fmt.Fprint(cw, "hello ")
	fmt.Fprint(cw, "world")
	cw.Close()

	if !bytes.Equal(tw.written[0], []byte("hello world")) && len(tw.written) != 1 {
		t.Errorf("expected writes to be coalesced, got %s", bytes.Join(tw.written, []byte(", ")))
	}

	if !tw.closed {
		t.Error("expected Close() to propagate")
	}
}

func TestTimeoutWriter(t *testing.T) {
	testWriter := &testWriter{written: make([][]byte, 0)}
	tw := NewTimeoutWriter(testWriter, 2*time.Millisecond)
	fmt.Fprint(tw, "hello world")
	timedout := new(bool)
	go func() {
		<-tw.Timeout
		*timedout = true
	}()
	time.Sleep(5 * time.Millisecond)

	if !*timedout {
		t.Errorf("expected TimeoutWriter to send a timeout")
	}
}

func TestLimitWriter(t *testing.T) {
	tw := &testWriter{written: make([][]byte, 0)}
	lw := NewLimitWriter(tw, 10)
	fmt.Fprintf(lw, "0123456789")

	limitReached := false
	select {
	case <-lw.LimitReached:
		limitReached = true
	default:
	}

	if limitReached {
		t.Error("expected limit to not have been reached after writing n bytes")
	}

	fmt.Fprintf(lw, "a")

	select {
	case <-lw.LimitReached:
		limitReached = true
	default:
	}

	if !limitReached {
		t.Error("expected limit to have been reached after writing n+1 bytes")
	}
}
<commit_msg>refactor(ioutils_test): remove a race condition in ioutils_test<commit_after>package main

import (
	"bytes"
	"fmt"
	"testing"
	"time"
)

type testWriter struct {
	written [][]byte
	closed  bool
}

func (t *testWriter) Write(p []byte) (int, error) {
	b := make([]byte, len(p))
	copy(b, p)
	t.written = append(t.written, b)
	return len(p), nil
}

func (t *testWriter) Close() error {
	t.closed = true
	return nil
}

func TestCoalesceWriteCloser(t *testing.T) {
	tw := &testWriter{written: make([][]byte, 0)}
	cw := NewCoalesceWriteCloser(tw)

	fmt.Fprint(cw, "hello ")
	fmt.Fprint(cw, "world")
	cw.Close()

	if !bytes.Equal(tw.written[0], []byte("hello world")) && len(tw.written) != 1 {
		t.Errorf("expected writes to be coalesced, got %s", bytes.Join(tw.written, []byte(", ")))
	}

	if !tw.closed {
		t.Error("expected Close() to propagate")
	}
}

func TestTimeoutWriter(t *testing.T) {
	testWriter := &testWriter{written: make([][]byte, 0)}
	tw := NewTimeoutWriter(testWriter, 2*time.Millisecond)
	fmt.Fprint(tw, "hello world")
	time.Sleep(5 * time.Millisecond)

	select {
	case <-tw.Timeout:
		// pass
	default:
		t.Errorf("expected TimeoutWriter to send a timeout")
	}
}

func TestLimitWriter(t *testing.T) {
	tw := &testWriter{written: make([][]byte, 0)}
	lw := NewLimitWriter(tw, 10)
	fmt.Fprintf(lw, "0123456789")

	limitReached := false
	select {
	case <-lw.LimitReached:
		limitReached = true
	default:
	}

	if limitReached {
		t.Error("expected limit to not have been reached after writing n bytes")
	}

	fmt.Fprintf(lw, "a")

	select {
	case <-lw.LimitReached:
		limitReached = true
	default:
	}

	if !limitReached {
		t.Error("expected limit to have been reached after writing n+1 bytes")
	}
}
<|endoftext|>


<commit_before><commit_msg>work in progress - add http service.<commit_after><|endoftext|>