<commit_before>// Copyright (C) 2017 Google Inc.
//
// 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 gles

import (
	"context"
	"fmt"
	"sort"

	"github.com/google/gapid/core/context/keys"
	"github.com/google/gapid/core/data/binary"
	"github.com/google/gapid/core/image"
	"github.com/google/gapid/core/log"
	"github.com/google/gapid/gapis/api"
	"github.com/google/gapid/gapis/api/transform"
	"github.com/google/gapid/gapis/messages"
	"github.com/google/gapid/gapis/replay"
	"github.com/google/gapid/gapis/replay/builder"
	"github.com/google/gapid/gapis/replay/value"
	"github.com/google/gapid/gapis/service"
)

type readFbTask struct {
	at   api.CmdID
	work func(ctx context.Context, w transform.Writer)
}

type readFramebuffer struct {
	tasks       []readFbTask
	tasksSorted bool
}

func newReadFramebuffer(ctx context.Context) *readFramebuffer {
	return &readFramebuffer{}
}

func (t *readFramebuffer) addTask(at api.CmdID, work func(context.Context, transform.Writer)) {
	t.tasks = append(t.tasks, readFbTask{at, work})
	t.tasksSorted = false
}

func (t *readFramebuffer) sortTasks() {
	if !t.tasksSorted {
		sort.Slice(t.tasks, func(i, j int) bool { return t.tasks[i].at < t.tasks[j].at })
		t.tasksSorted = true
	}
}

func (t *readFramebuffer) Transform(ctx context.Context, id api.CmdID, cmd api.Cmd, out transform.Writer) {
	if id.IsReal() {
		t.sortTasks()
		for len(t.tasks) > 0 && t.tasks[0].at < id {
			t.tasks[0].work(ctx, out)
			t.tasks = t.tasks[1:]
		}
	}
	out.MutateAndWrite(ctx, id, cmd)
}

func (t *readFramebuffer) Flush(ctx context.Context, out transform.Writer) {
	t.sortTasks()
	for _, task := range t.tasks {
		task.work(ctx, out)
	}
	t.tasks = nil
}

func (t *readFramebuffer) depth(
	id api.CmdID,
	thread uint64,
	fb FramebufferId,
	res replay.Result) {

	t.addTask(id, func(ctx context.Context, out transform.Writer) {
		s := out.State()
		c := GetContext(s, thread)

		if fb == 0 {
			fb = c.Bound.DrawFramebuffer.GetID()
		}

		width, height, format, err := GetState(s).getFramebufferAttachmentInfo(thread, fb, api.FramebufferAttachment_Depth)
		if err != nil {
			log.W(ctx, "Failed to read framebuffer after cmd %v: %v", id, err)
			res(nil, &service.ErrDataUnavailable{Reason: messages.ErrFramebufferUnavailable()})
			return
		}

		t := newTweaker(out, id.Derived(), CommandBuilder{Thread: thread})
		defer t.revert(ctx)
		t.glBindFramebuffer_Read(ctx, fb)

		postColorData(ctx, s, int32(width), int32(height), format, out, id, thread, res)
	})
}

func (t *readFramebuffer) color(
	id api.CmdID,
	thread uint64,
	width, height uint32,
	fb FramebufferId,
	bufferIdx uint32,
	res replay.Result) {

	t.addTask(id, func(ctx context.Context, out transform.Writer) {
		s := out.State()
		c := GetContext(s, thread)

		if fb == 0 {
			fb = c.Bound.DrawFramebuffer.GetID()
		}

		attachment := api.FramebufferAttachment_Color0 + api.FramebufferAttachment(bufferIdx)
		w, h, fmt, err := GetState(s).getFramebufferAttachmentInfo(thread, fb, attachment)
		if err != nil {
			log.W(ctx, "Failed to read framebuffer after cmd %v: %v", id, err)
			res(nil, &service.ErrDataUnavailable{Reason: messages.ErrFramebufferUnavailable()})
			return
		}
		if fmt == 0 {
			log.W(ctx, "Failed to read framebuffer after cmd %v: no image format", id)
			res(nil, &service.ErrDataUnavailable{Reason: messages.ErrFramebufferUnavailable()})
			return
		}

		var (
			inW  = int32(w)
			inH  = int32(h)
			outW = int32(width)
			outH = int32(height)
		)

		dID := id.Derived()
		cb := CommandBuilder{Thread: thread}
		t := newTweaker(out, dID, cb)
		defer t.revert(ctx)
		t.glBindFramebuffer_Read(ctx, fb)

		// TODO: These glReadBuffer calls need to be changed for on-device
		//       replay. Note that glReadBuffer was only introduced in
		//       OpenGL ES 3.0, and that GL_FRONT is not a legal enum value.
		if c.Bound.DrawFramebuffer == c.Objects.Default.Framebuffer {
			out.MutateAndWrite(ctx, dID, cb.Custom(func(ctx context.Context, s *api.State, b *builder.Builder) error {
				// TODO: We assume here that the default framebuffer is
				//       single-buffered. Once we support double-buffering we
				//       need to decide whether to read from GL_FRONT or GL_BACK.
				cb.GlReadBuffer(GLenum_GL_FRONT).Call(ctx, s, b)
				return nil
			}))
		} else {
			t.glReadBuffer(ctx, GLenum_GL_COLOR_ATTACHMENT0+GLenum(bufferIdx))
		}

		if inW == outW && inH == outH {
			postColorData(ctx, s, outW, outH, fmt, out, id, thread, res)
		} else {
			t.glScissor(ctx, 0, 0, GLsizei(inW), GLsizei(inH))
			framebufferID := t.glGenFramebuffer(ctx)
			t.glBindFramebuffer_Draw(ctx, framebufferID)
			renderbufferID := t.glGenRenderbuffer(ctx)
			t.glBindRenderbuffer(ctx, renderbufferID)

			mutateAndWriteEach(ctx, out, dID,
				cb.GlRenderbufferStorage(GLenum_GL_RENDERBUFFER, fmt, GLsizei(outW), GLsizei(outH)),
				cb.GlFramebufferRenderbuffer(GLenum_GL_DRAW_FRAMEBUFFER, GLenum_GL_COLOR_ATTACHMENT0, GLenum_GL_RENDERBUFFER, renderbufferID),
				cb.GlBlitFramebuffer(0, 0, GLint(inW), GLint(inH), 0, 0, GLint(outW), GLint(outH), GLbitfield_GL_COLOR_BUFFER_BIT, GLenum_GL_LINEAR),
			)
			t.glBindFramebuffer_Read(ctx, framebufferID)

			postColorData(ctx, s, outW, outH, fmt, out, id, thread, res)
		}

	})
}

func postColorData(ctx context.Context,
	s *api.State,
	width, height int32,
	sizedFormat GLenum,
	out transform.Writer,
	id api.CmdID,
	thread uint64,
	res replay.Result) {

	unsizedFormat, ty := getUnsizedFormatAndType(sizedFormat)
	imgFmt, err := getImageFormat(unsizedFormat, ty)
	if err != nil {
		res(nil, err)
		return
	}

	dID := id.Derived()
	cb := CommandBuilder{Thread: thread}
	t := newTweaker(out, dID, cb)
	t.setPackStorage(ctx, PixelStorageState{Alignment: 1}, 0)

	imageSize := imgFmt.Size(int(width), int(height), 1)
	tmp := s.AllocOrPanic(ctx, uint64(imageSize))
	out.MutateAndWrite(ctx, dID, cb.Custom(func(ctx context.Context, s *api.State, b *builder.Builder) error {
		// TODO: We use Call() directly here because we are calling glReadPixels
		// with depth formats which are not legal for GLES. Once we're replaying
		// on-device again, we need to take a look at methods for reading the
		// depth buffer.

		b.ReserveMemory(tmp.Range())
		cb.GlReadPixels(0, 0, GLsizei(width), GLsizei(height), unsizedFormat, ty, tmp.Ptr()).
			Call(ctx, s, b)

		b.Post(value.ObservedPointer(tmp.Address()), uint64(imageSize), func(r binary.Reader, err error) error {
			var data []byte
			if err == nil {
				data = make([]byte, imageSize)
				r.Data(data)
				err = r.Error()
			}
			if err != nil {
				err = fmt.Errorf("Could not read framebuffer data (expected length %d bytes): %v", imageSize, err)
				data = nil
			}
			img := &image.Data{
				Bytes:  data,
				Width:  uint32(width),
				Height: uint32(height),
				Depth:  1,
				Format: imgFmt,
			}
			res(img, err)
			return err
		})
		return nil
	}))
	tmp.Free()

	out.MutateAndWrite(ctx, dID, cb.GlGetError(0)) // Check for errors.

	t.revert(ctx)
}

func mutateAndWriteEach(ctx context.Context, out transform.Writer, id api.CmdID, cmds ...api.Cmd) {
	for _, cmd := range cmds {
		out.MutateAndWrite(ctx, id, cmd)
	}
}

type nextUnusedIDKeyTy string

const nextUnusedIDKey = nextUnusedIDKeyTy("nextUnusedID")

func PutUnusedIDMap(ctx context.Context) context.Context {
	return keys.WithValue(ctx, nextUnusedIDKey, map[rune]uint32{})
}

// newUnusedID returns temporary object ID.
// The tag makes the IDs for given object type more deterministic.
func newUnusedID(ctx context.Context, tag rune, existenceTest func(uint32) bool) uint32 {
	val := ctx.Value(nextUnusedIDKey)
	if val == nil {
		panic(nextUnusedIDKey + " missing from context")
	}
	nextUnusedID := val.(map[rune]uint32)

	// Use the tag to allocate from different ranges.
	prefix := uint32(tag)
	if prefix == 0 || prefix > 128 {
		panic(fmt.Errorf("Expected ASCII character"))
	}
	prefix = prefix * 10000000
	// Get the next ID and make sure it is free.
	for {
		nextUnusedID[tag]++
		x := prefix + nextUnusedID[tag]
		if !existenceTest(x) && x != 0 {
			return x
		}
	}
}
<commit_msg>gles: Fix panic in read_framebuffer if there's no bound fb<commit_after>// Copyright (C) 2017 Google Inc.
//
// 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 gles

import (
	"context"
	"fmt"
	"sort"

	"github.com/google/gapid/core/context/keys"
	"github.com/google/gapid/core/data/binary"
	"github.com/google/gapid/core/image"
	"github.com/google/gapid/core/log"
	"github.com/google/gapid/gapis/api"
	"github.com/google/gapid/gapis/api/transform"
	"github.com/google/gapid/gapis/messages"
	"github.com/google/gapid/gapis/replay"
	"github.com/google/gapid/gapis/replay/builder"
	"github.com/google/gapid/gapis/replay/value"
	"github.com/google/gapid/gapis/service"
)

type readFbTask struct {
	at   api.CmdID
	work func(ctx context.Context, w transform.Writer)
}

type readFramebuffer struct {
	tasks       []readFbTask
	tasksSorted bool
}

func newReadFramebuffer(ctx context.Context) *readFramebuffer {
	return &readFramebuffer{}
}

func (t *readFramebuffer) addTask(at api.CmdID, work func(context.Context, transform.Writer)) {
	t.tasks = append(t.tasks, readFbTask{at, work})
	t.tasksSorted = false
}

func (t *readFramebuffer) sortTasks() {
	if !t.tasksSorted {
		sort.Slice(t.tasks, func(i, j int) bool { return t.tasks[i].at < t.tasks[j].at })
		t.tasksSorted = true
	}
}

func (t *readFramebuffer) Transform(ctx context.Context, id api.CmdID, cmd api.Cmd, out transform.Writer) {
	if id.IsReal() {
		t.sortTasks()
		for len(t.tasks) > 0 && t.tasks[0].at < id {
			t.tasks[0].work(ctx, out)
			t.tasks = t.tasks[1:]
		}
	}
	out.MutateAndWrite(ctx, id, cmd)
}

func (t *readFramebuffer) Flush(ctx context.Context, out transform.Writer) {
	t.sortTasks()
	for _, task := range t.tasks {
		task.work(ctx, out)
	}
	t.tasks = nil
}

func (t *readFramebuffer) depth(
	id api.CmdID,
	thread uint64,
	fb FramebufferId,
	res replay.Result) {

	t.addTask(id, func(ctx context.Context, out transform.Writer) {
		s := out.State()
		c := GetContext(s, thread)

		if fb == 0 {
			if c.Bound.DrawFramebuffer == nil {
				log.W(ctx, "No framebuffer bound after %v", id)
				res(nil, &service.ErrDataUnavailable{Reason: messages.ErrFramebufferUnavailable()})
				return
			}
			fb = c.Bound.DrawFramebuffer.GetID()
		}

		width, height, format, err := GetState(s).getFramebufferAttachmentInfo(thread, fb, api.FramebufferAttachment_Depth)
		if err != nil {
			log.W(ctx, "Failed to read framebuffer after cmd %v: %v", id, err)
			res(nil, &service.ErrDataUnavailable{Reason: messages.ErrFramebufferUnavailable()})
			return
		}

		t := newTweaker(out, id.Derived(), CommandBuilder{Thread: thread})
		defer t.revert(ctx)
		t.glBindFramebuffer_Read(ctx, fb)

		postColorData(ctx, s, int32(width), int32(height), format, out, id, thread, res)
	})
}

func (t *readFramebuffer) color(
	id api.CmdID,
	thread uint64,
	width, height uint32,
	fb FramebufferId,
	bufferIdx uint32,
	res replay.Result) {

	t.addTask(id, func(ctx context.Context, out transform.Writer) {
		s := out.State()
		c := GetContext(s, thread)

		if fb == 0 {
			if c.Bound.DrawFramebuffer == nil {
				log.W(ctx, "No framebuffer bound after %v", id)
				res(nil, &service.ErrDataUnavailable{Reason: messages.ErrFramebufferUnavailable()})
				return
			}
			fb = c.Bound.DrawFramebuffer.GetID()
		}

		attachment := api.FramebufferAttachment_Color0 + api.FramebufferAttachment(bufferIdx)
		w, h, fmt, err := GetState(s).getFramebufferAttachmentInfo(thread, fb, attachment)
		if err != nil {
			log.W(ctx, "Failed to read framebuffer after cmd %v: %v", id, err)
			res(nil, &service.ErrDataUnavailable{Reason: messages.ErrFramebufferUnavailable()})
			return
		}
		if fmt == 0 {
			log.W(ctx, "Failed to read framebuffer after cmd %v: no image format", id)
			res(nil, &service.ErrDataUnavailable{Reason: messages.ErrFramebufferUnavailable()})
			return
		}

		var (
			inW  = int32(w)
			inH  = int32(h)
			outW = int32(width)
			outH = int32(height)
		)

		dID := id.Derived()
		cb := CommandBuilder{Thread: thread}
		t := newTweaker(out, dID, cb)
		defer t.revert(ctx)
		t.glBindFramebuffer_Read(ctx, fb)

		// TODO: These glReadBuffer calls need to be changed for on-device
		//       replay. Note that glReadBuffer was only introduced in
		//       OpenGL ES 3.0, and that GL_FRONT is not a legal enum value.
		if c.Bound.DrawFramebuffer == c.Objects.Default.Framebuffer {
			out.MutateAndWrite(ctx, dID, cb.Custom(func(ctx context.Context, s *api.State, b *builder.Builder) error {
				// TODO: We assume here that the default framebuffer is
				//       single-buffered. Once we support double-buffering we
				//       need to decide whether to read from GL_FRONT or GL_BACK.
				cb.GlReadBuffer(GLenum_GL_FRONT).Call(ctx, s, b)
				return nil
			}))
		} else {
			t.glReadBuffer(ctx, GLenum_GL_COLOR_ATTACHMENT0+GLenum(bufferIdx))
		}

		if inW == outW && inH == outH {
			postColorData(ctx, s, outW, outH, fmt, out, id, thread, res)
		} else {
			t.glScissor(ctx, 0, 0, GLsizei(inW), GLsizei(inH))
			framebufferID := t.glGenFramebuffer(ctx)
			t.glBindFramebuffer_Draw(ctx, framebufferID)
			renderbufferID := t.glGenRenderbuffer(ctx)
			t.glBindRenderbuffer(ctx, renderbufferID)

			mutateAndWriteEach(ctx, out, dID,
				cb.GlRenderbufferStorage(GLenum_GL_RENDERBUFFER, fmt, GLsizei(outW), GLsizei(outH)),
				cb.GlFramebufferRenderbuffer(GLenum_GL_DRAW_FRAMEBUFFER, GLenum_GL_COLOR_ATTACHMENT0, GLenum_GL_RENDERBUFFER, renderbufferID),
				cb.GlBlitFramebuffer(0, 0, GLint(inW), GLint(inH), 0, 0, GLint(outW), GLint(outH), GLbitfield_GL_COLOR_BUFFER_BIT, GLenum_GL_LINEAR),
			)
			t.glBindFramebuffer_Read(ctx, framebufferID)

			postColorData(ctx, s, outW, outH, fmt, out, id, thread, res)
		}

	})
}

func postColorData(ctx context.Context,
	s *api.State,
	width, height int32,
	sizedFormat GLenum,
	out transform.Writer,
	id api.CmdID,
	thread uint64,
	res replay.Result) {

	unsizedFormat, ty := getUnsizedFormatAndType(sizedFormat)
	imgFmt, err := getImageFormat(unsizedFormat, ty)
	if err != nil {
		res(nil, err)
		return
	}

	dID := id.Derived()
	cb := CommandBuilder{Thread: thread}
	t := newTweaker(out, dID, cb)
	t.setPackStorage(ctx, PixelStorageState{Alignment: 1}, 0)

	imageSize := imgFmt.Size(int(width), int(height), 1)
	tmp := s.AllocOrPanic(ctx, uint64(imageSize))
	out.MutateAndWrite(ctx, dID, cb.Custom(func(ctx context.Context, s *api.State, b *builder.Builder) error {
		// TODO: We use Call() directly here because we are calling glReadPixels
		// with depth formats which are not legal for GLES. Once we're replaying
		// on-device again, we need to take a look at methods for reading the
		// depth buffer.

		b.ReserveMemory(tmp.Range())
		cb.GlReadPixels(0, 0, GLsizei(width), GLsizei(height), unsizedFormat, ty, tmp.Ptr()).
			Call(ctx, s, b)

		b.Post(value.ObservedPointer(tmp.Address()), uint64(imageSize), func(r binary.Reader, err error) error {
			var data []byte
			if err == nil {
				data = make([]byte, imageSize)
				r.Data(data)
				err = r.Error()
			}
			if err != nil {
				err = fmt.Errorf("Could not read framebuffer data (expected length %d bytes): %v", imageSize, err)
				data = nil
			}
			img := &image.Data{
				Bytes:  data,
				Width:  uint32(width),
				Height: uint32(height),
				Depth:  1,
				Format: imgFmt,
			}
			res(img, err)
			return err
		})
		return nil
	}))
	tmp.Free()

	out.MutateAndWrite(ctx, dID, cb.GlGetError(0)) // Check for errors.

	t.revert(ctx)
}

func mutateAndWriteEach(ctx context.Context, out transform.Writer, id api.CmdID, cmds ...api.Cmd) {
	for _, cmd := range cmds {
		out.MutateAndWrite(ctx, id, cmd)
	}
}

type nextUnusedIDKeyTy string

const nextUnusedIDKey = nextUnusedIDKeyTy("nextUnusedID")

func PutUnusedIDMap(ctx context.Context) context.Context {
	return keys.WithValue(ctx, nextUnusedIDKey, map[rune]uint32{})
}

// newUnusedID returns temporary object ID.
// The tag makes the IDs for given object type more deterministic.
func newUnusedID(ctx context.Context, tag rune, existenceTest func(uint32) bool) uint32 {
	val := ctx.Value(nextUnusedIDKey)
	if val == nil {
		panic(nextUnusedIDKey + " missing from context")
	}
	nextUnusedID := val.(map[rune]uint32)

	// Use the tag to allocate from different ranges.
	prefix := uint32(tag)
	if prefix == 0 || prefix > 128 {
		panic(fmt.Errorf("Expected ASCII character"))
	}
	prefix = prefix * 10000000
	// Get the next ID and make sure it is free.
	for {
		nextUnusedID[tag]++
		x := prefix + nextUnusedID[tag]
		if !existenceTest(x) && x != 0 {
			return x
		}
	}
}
<|endoftext|>


<commit_before>package provisioning

import (
	"context"
	"log"
	"net"
	"net/url"
	"os"
	"path/filepath"

	"github.com/n0stack/n0core/pkg/driver/qemu_img"

	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"

	"github.com/golang/protobuf/ptypes/empty"
	"github.com/n0stack/n0core/pkg/driver/iproute2"
	"github.com/n0stack/n0core/pkg/driver/qemu"
	"github.com/pkg/errors"
	uuid "github.com/satori/go.uuid"
)

var N0coreVirtualMachineNamespace uuid.UUID

const (
	QMPMonitorSocketFile   = "monitor.sock"
	VNCWebSocketPortOffset = 6900
)

func init() {
	N0coreVirtualMachineNamespace, _ = uuid.FromString("a015d18d-b2c3-4181-8028-6f707ef31c95")
}

type VirtualMachineAgentAPI struct {
	baseDirectory string
}

func CreateVirtualMachineAgentAPI(basedir string) (*VirtualMachineAgentAPI, error) {
	b, err := filepath.Abs(basedir)
	if err != nil {
		return nil, errors.Wrap(err, "Failed to get absolute path")
	}

	if _, err := os.Stat(b); os.IsNotExist(err) {
		if err := os.MkdirAll(b, 0644); err != nil { // TODO: check permission
			return nil, errors.Wrapf(err, "Failed to mkdir '%s'", b)
		}
	}

	return &VirtualMachineAgentAPI{
		baseDirectory: b,
	}, nil
}

func (a VirtualMachineAgentAPI) GetWorkDirectory(name string) (string, error) {
	p := filepath.Join(a.baseDirectory, name)

	if _, err := os.Stat(p); os.IsNotExist(err) {
		if err := os.MkdirAll(p, 0644); err != nil { // TODO: check permission
			return p, errors.Wrapf(err, "Failed to mkdir '%s'", p)
		}
	}

	return p, nil
}

func (a VirtualMachineAgentAPI) CreateVirtualMachineAgent(ctx context.Context, req *CreateVirtualMachineAgentRequest) (*VirtualMachineAgent, error) {
	id := uuid.NewV5(N0coreVirtualMachineNamespace, req.Name)
	q, err := qemu.OpenQemu(&id)
	if err != nil {
		return nil, grpc.Errorf(codes.Internal, "Failed to open qemu process: %s", err.Error())
	}
	if q.IsRunning() {
		return nil, grpc.Errorf(codes.AlreadyExists, "")
	}

	wd, err := a.GetWorkDirectory(req.Name)
	if err != nil {
		return nil, grpc.Errorf(codes.Internal, "Failed to get working directory '%s'", wd)
	}
	websocket := qemu.GetNewListenPort(VNCWebSocketPortOffset)

	if err := q.Start(req.Name, filepath.Join(wd, QMPMonitorSocketFile), websocket, req.Vcpus, req.MemoryBytes); err != nil {
		log.Printf("Failed to start qemu process: err=%s", err.Error())
		return nil, grpc.Errorf(codes.Internal, "Failed to start qemu process")
	}

	for _, nd := range req.Netdev {
		b, err := iproute2.NewBridge(TrimNetdevName(nd.NetworkName))
		if err != nil {
			log.Printf("Failed to create bridge '%s': err='%s'", nd.NetworkName, err.Error())
			return nil, grpc.Errorf(codes.Internal, "") // TODO #89
		}

		t, err := iproute2.NewTap(TrimNetdevName(nd.Name))
		if err != nil {
			log.Printf("Failed to create tap '%s': err='%s'", nd.Name, err.Error())
			return nil, grpc.Errorf(codes.Internal, "") // TODO #89
		}
		if err := t.SetMaster(b); err != nil {
			log.Printf("Failed to set master of tap '%s' as '%s': err='%s'", t.Name(), b.Name(), err.Error())
			return nil, grpc.Errorf(codes.Internal, "") // TODO #89
		}

		hw, err := net.ParseMAC(nd.HardwareAddress)
		if err != nil {
			return nil, grpc.Errorf(codes.InvalidArgument, "Hardware address '%s' is invalid on netdev '%s'", nd.HardwareAddress, nd.Name)
		}

		if err := q.AttachTap(nd.Name, t.Name(), hw); err != nil {
			log.Printf("Failed to attach tap: err='%s'", err.Error())
			return nil, grpc.Errorf(codes.Internal, "Failed to attach tap")
		}
	}

	for _, bd := range req.Blockdev {
		u, err := url.Parse(bd.Url)
		if err != nil {
			return nil, grpc.Errorf(codes.InvalidArgument, "url '%s' is invalid url: '%s'", bd.Url, err.Error())
		}

		i, err := img.OpenQemuImg(u.Path)
		if err != nil {
			log.Printf("Failed to open qemu image: err='%s'", err.Error())
			return nil, grpc.Errorf(codes.Internal, "") // TODO #89
		}

		// この条件は雑
		if i.Info.Format == "raw" {
			if err := q.AttachISO(bd.Name, u, uint(bd.BootIndex)); err != nil {
				log.Printf("Failed to attach iso '%s': err='%s'", u.Path, err.Error())
				return nil, grpc.Errorf(codes.Internal, "") // TODO #89
			}
		} else {
			if err := q.AttachQcow2(bd.Name, u, uint(bd.BootIndex)); err != nil {
				log.Printf("Failed to attach image '%s': err='%s'", u.Path, err.Error())
				return nil, grpc.Errorf(codes.Internal, "") // TODO #89
			}
		}
	}

	if err := q.Boot(); err != nil {
		log.Printf("Failed to boot qemu: err=%s", err.Error())
		return nil, grpc.Errorf(codes.Internal, "Failed to boot qemu")
	}

	res := &VirtualMachineAgent{
		Name:          req.Name,
		Uuid:          id.String(),
		Vcpus:         req.Vcpus,
		MemoryBytes:   req.MemoryBytes,
		Blockdev:      req.Blockdev,
		Netdev:        req.Netdev,
		WebsocketPort: uint32(websocket),
	}
	if s, err := q.Status(); err != nil {
		return nil, grpc.Errorf(codes.Internal, "Failed to get status")
	} else {
		res.State = GetAgentStateFromQemuState(s)
	}
	return res, nil
}

func (a VirtualMachineAgentAPI) DeleteVirtualMachineAgent(ctx context.Context, req *DeleteVirtualMachineAgentRequest) (*empty.Empty, error) {
	id := uuid.NewV5(N0coreVirtualMachineNamespace, req.Name)
	q, err := qemu.OpenQemu(&id)
	if err != nil {
		return nil, grpc.Errorf(codes.Internal, "Failed to open qemu process: %s", err.Error())
	}
	if !q.IsRunning() {
		return nil, grpc.Errorf(codes.NotFound, "")
	}

	if err := q.Delete(); err != nil {
		return nil, grpc.Errorf(codes.Internal, "Failed to delete qemu: %s", err.Error())
	}

	for _, nd := range req.Netdev {
		t, err := iproute2.NewTap(TrimNetdevName(nd.Name))
		if err != nil {
			log.Printf("Failed to create tap '%s': err='%s'", nd.Name, err.Error())
			return nil, grpc.Errorf(codes.Internal, "") // TODO #89
		}

		if err := t.Delete(); err != nil {
			log.Printf("Failed to delete tap '%s': err='%s'", nd.Name, err.Error())
			return nil, grpc.Errorf(codes.Internal, "") // TODO #89
		}

		b, err := iproute2.NewBridge(TrimNetdevName(nd.NetworkName))
		if err != nil {
			log.Printf("Failed to create bridge '%s': err='%s'", nd.NetworkName, err.Error())
			return nil, grpc.Errorf(codes.Internal, "") // TODO #89
		}

		links, err := b.ListSlaves()
		if err != nil {
			log.Printf("Failed to list links of bridge '%s': err='%s'", nd.NetworkName, err.Error())
			return nil, grpc.Errorf(codes.Internal, "") // TODO #89
		}

		// TODO: 以下遅い気がする
		i := 0
		for _, l := range links {
			if _, err := iproute2.NewTap(l); err == nil {
				i++
			}
		}
		if i == 0 {
			if err := b.Delete(); err != nil {
				log.Printf("Failed to delete bridge '%s': err='%s'", b.Name(), err.Error())
				return nil, grpc.Errorf(codes.Internal, "") // TODO #89
			}
		}
	}

	return &empty.Empty{}, nil
}

func (a VirtualMachineAgentAPI) BootVirtualMachineAgent(ctx context.Context, req *BootVirtualMachineAgentRequest) (*BootVirtualMachineAgentResponse, error) {
	id := uuid.NewV5(N0coreVirtualMachineNamespace, req.Name)
	q, err := qemu.OpenQemu(&id)
	if err != nil {
		return nil, grpc.Errorf(codes.Internal, "Failed to open qemu process: %s", err.Error())
	}

	if !q.IsRunning() {
		return nil, grpc.Errorf(codes.NotFound, "")
	}

	if err := q.Boot(); err != nil {
		return nil, grpc.Errorf(codes.Internal, "Failed to boot qemu: %s", err.Error())
	}

	s, err := q.Status()
	if err != nil {
		return nil, grpc.Errorf(codes.Internal, "Failed to get qemu status: %s", err.Error())
	}

	return &BootVirtualMachineAgentResponse{
		State: GetAgentStateFromQemuState(s),
	}, nil
}

func (a VirtualMachineAgentAPI) RebootVirtualMachineAgent(ctx context.Context, req *RebootVirtualMachineAgentRequest) (*RebootVirtualMachineAgentResponse, error) {
	id := uuid.NewV5(N0coreVirtualMachineNamespace, req.Name)
	q, err := qemu.OpenQemu(&id)
	if err != nil {
		return nil, grpc.Errorf(codes.Internal, "Failed to open qemu process: %s", err.Error())
	}

	if !q.IsRunning() {
		return nil, grpc.Errorf(codes.NotFound, "")
	}

	if req.Hard {
		if err := q.HardReset(); err != nil {
			return nil, grpc.Errorf(codes.Internal, "Failed to hard reboot qemu: %s", err.Error())
		}
	} else {
		return nil, grpc.Errorf(codes.Unimplemented, "reboot is unimplemented")
	}

	s, err := q.Status()
	if err != nil {
		return nil, grpc.Errorf(codes.Internal, "Failed to get qemu status: %s", err.Error())
	}

	return &RebootVirtualMachineAgentResponse{
		State: GetAgentStateFromQemuState(s),
	}, nil
}

func (a VirtualMachineAgentAPI) ShutdownVirtualMachineAgent(ctx context.Context, req *ShutdownVirtualMachineAgentRequest) (*ShutdownVirtualMachineAgentResponse, error) {
	id := uuid.NewV5(N0coreVirtualMachineNamespace, req.Name)
	q, err := qemu.OpenQemu(&id)
	if err != nil {
		return nil, grpc.Errorf(codes.Internal, "Failed to open qemu process: %s", err.Error())
	}

	if !q.IsRunning() {
		return nil, grpc.Errorf(codes.NotFound, "")
	}

	if req.Hard {
		if err := q.HardShutdown(); err != nil {
			return nil, grpc.Errorf(codes.Internal, "Failed to hard shutdown qemu: %s", err.Error())
		}
	} else {
		if err := q.Shutdown(); err != nil {
			return nil, grpc.Errorf(codes.Internal, "Failed to shutdown qemu: %s", err.Error())
		}
	}

	s, err := q.Status()
	if err != nil {
		return nil, grpc.Errorf(codes.Internal, "Failed to get qemu status: %s", err.Error())
	}

	return &ShutdownVirtualMachineAgentResponse{
		State: GetAgentStateFromQemuState(s),
	}, nil
}
<commit_msg>forgot close qemu monitor<commit_after>package provisioning

import (
	"context"
	"log"
	"net"
	"net/url"
	"os"
	"path/filepath"

	"github.com/n0stack/n0core/pkg/driver/qemu_img"

	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"

	"github.com/golang/protobuf/ptypes/empty"
	"github.com/n0stack/n0core/pkg/driver/iproute2"
	"github.com/n0stack/n0core/pkg/driver/qemu"
	"github.com/pkg/errors"
	uuid "github.com/satori/go.uuid"
)

var N0coreVirtualMachineNamespace uuid.UUID

const (
	QMPMonitorSocketFile   = "monitor.sock"
	VNCWebSocketPortOffset = 6900
)

func init() {
	N0coreVirtualMachineNamespace, _ = uuid.FromString("a015d18d-b2c3-4181-8028-6f707ef31c95")
}

type VirtualMachineAgentAPI struct {
	baseDirectory string
}

func CreateVirtualMachineAgentAPI(basedir string) (*VirtualMachineAgentAPI, error) {
	b, err := filepath.Abs(basedir)
	if err != nil {
		return nil, errors.Wrap(err, "Failed to get absolute path")
	}

	if _, err := os.Stat(b); os.IsNotExist(err) {
		if err := os.MkdirAll(b, 0644); err != nil { // TODO: check permission
			return nil, errors.Wrapf(err, "Failed to mkdir '%s'", b)
		}
	}

	return &VirtualMachineAgentAPI{
		baseDirectory: b,
	}, nil
}

func (a VirtualMachineAgentAPI) GetWorkDirectory(name string) (string, error) {
	p := filepath.Join(a.baseDirectory, name)

	if _, err := os.Stat(p); os.IsNotExist(err) {
		if err := os.MkdirAll(p, 0644); err != nil { // TODO: check permission
			return p, errors.Wrapf(err, "Failed to mkdir '%s'", p)
		}
	}

	return p, nil
}

func (a VirtualMachineAgentAPI) CreateVirtualMachineAgent(ctx context.Context, req *CreateVirtualMachineAgentRequest) (*VirtualMachineAgent, error) {
	id := uuid.NewV5(N0coreVirtualMachineNamespace, req.Name)
	q, err := qemu.OpenQemu(&id)
	if err != nil {
		return nil, grpc.Errorf(codes.Internal, "Failed to open qemu process: %s", err.Error())
	}
	if q.IsRunning() {
		return nil, grpc.Errorf(codes.AlreadyExists, "")
	}

	wd, err := a.GetWorkDirectory(req.Name)
	if err != nil {
		return nil, grpc.Errorf(codes.Internal, "Failed to get working directory '%s'", wd)
	}
	websocket := qemu.GetNewListenPort(VNCWebSocketPortOffset)

	if err := q.Start(req.Name, filepath.Join(wd, QMPMonitorSocketFile), websocket, req.Vcpus, req.MemoryBytes); err != nil {
		log.Printf("Failed to start qemu process: err=%s", err.Error())
		return nil, grpc.Errorf(codes.Internal, "Failed to start qemu process")
	}
	defer q.Close()

	for _, nd := range req.Netdev {
		b, err := iproute2.NewBridge(TrimNetdevName(nd.NetworkName))
		if err != nil {
			log.Printf("Failed to create bridge '%s': err='%s'", nd.NetworkName, err.Error())
			return nil, grpc.Errorf(codes.Internal, "") // TODO #89
		}

		t, err := iproute2.NewTap(TrimNetdevName(nd.Name))
		if err != nil {
			log.Printf("Failed to create tap '%s': err='%s'", nd.Name, err.Error())
			return nil, grpc.Errorf(codes.Internal, "") // TODO #89
		}
		if err := t.SetMaster(b); err != nil {
			log.Printf("Failed to set master of tap '%s' as '%s': err='%s'", t.Name(), b.Name(), err.Error())
			return nil, grpc.Errorf(codes.Internal, "") // TODO #89
		}

		hw, err := net.ParseMAC(nd.HardwareAddress)
		if err != nil {
			return nil, grpc.Errorf(codes.InvalidArgument, "Hardware address '%s' is invalid on netdev '%s'", nd.HardwareAddress, nd.Name)
		}

		if err := q.AttachTap(nd.Name, t.Name(), hw); err != nil {
			log.Printf("Failed to attach tap: err='%s'", err.Error())
			return nil, grpc.Errorf(codes.Internal, "Failed to attach tap")
		}
	}

	for _, bd := range req.Blockdev {
		u, err := url.Parse(bd.Url)
		if err != nil {
			return nil, grpc.Errorf(codes.InvalidArgument, "url '%s' is invalid url: '%s'", bd.Url, err.Error())
		}

		i, err := img.OpenQemuImg(u.Path)
		if err != nil {
			log.Printf("Failed to open qemu image: err='%s'", err.Error())
			return nil, grpc.Errorf(codes.Internal, "") // TODO #89
		}

		// この条件は雑
		if i.Info.Format == "raw" {
			if err := q.AttachISO(bd.Name, u, uint(bd.BootIndex)); err != nil {
				log.Printf("Failed to attach iso '%s': err='%s'", u.Path, err.Error())
				return nil, grpc.Errorf(codes.Internal, "") // TODO #89
			}
		} else {
			if err := q.AttachQcow2(bd.Name, u, uint(bd.BootIndex)); err != nil {
				log.Printf("Failed to attach image '%s': err='%s'", u.Path, err.Error())
				return nil, grpc.Errorf(codes.Internal, "") // TODO #89
			}
		}
	}

	if err := q.Boot(); err != nil {
		log.Printf("Failed to boot qemu: err=%s", err.Error())
		return nil, grpc.Errorf(codes.Internal, "Failed to boot qemu")
	}

	res := &VirtualMachineAgent{
		Name:          req.Name,
		Uuid:          id.String(),
		Vcpus:         req.Vcpus,
		MemoryBytes:   req.MemoryBytes,
		Blockdev:      req.Blockdev,
		Netdev:        req.Netdev,
		WebsocketPort: uint32(websocket),
	}
	if s, err := q.Status(); err != nil {
		return nil, grpc.Errorf(codes.Internal, "Failed to get status")
	} else {
		res.State = GetAgentStateFromQemuState(s)
	}
	return res, nil
}

func (a VirtualMachineAgentAPI) DeleteVirtualMachineAgent(ctx context.Context, req *DeleteVirtualMachineAgentRequest) (*empty.Empty, error) {
	id := uuid.NewV5(N0coreVirtualMachineNamespace, req.Name)
	q, err := qemu.OpenQemu(&id)
	if err != nil {
		return nil, grpc.Errorf(codes.Internal, "Failed to open qemu process: %s", err.Error())
	}
	if !q.IsRunning() {
		return nil, grpc.Errorf(codes.NotFound, "")
	}

	if err := q.Delete(); err != nil {
		return nil, grpc.Errorf(codes.Internal, "Failed to delete qemu: %s", err.Error())
	}

	for _, nd := range req.Netdev {
		t, err := iproute2.NewTap(TrimNetdevName(nd.Name))
		if err != nil {
			log.Printf("Failed to create tap '%s': err='%s'", nd.Name, err.Error())
			return nil, grpc.Errorf(codes.Internal, "") // TODO #89
		}

		if err := t.Delete(); err != nil {
			log.Printf("Failed to delete tap '%s': err='%s'", nd.Name, err.Error())
			return nil, grpc.Errorf(codes.Internal, "") // TODO #89
		}

		b, err := iproute2.NewBridge(TrimNetdevName(nd.NetworkName))
		if err != nil {
			log.Printf("Failed to create bridge '%s': err='%s'", nd.NetworkName, err.Error())
			return nil, grpc.Errorf(codes.Internal, "") // TODO #89
		}

		links, err := b.ListSlaves()
		if err != nil {
			log.Printf("Failed to list links of bridge '%s': err='%s'", nd.NetworkName, err.Error())
			return nil, grpc.Errorf(codes.Internal, "") // TODO #89
		}

		// TODO: 以下遅い気がする
		i := 0
		for _, l := range links {
			if _, err := iproute2.NewTap(l); err == nil {
				i++
			}
		}
		if i == 0 {
			if err := b.Delete(); err != nil {
				log.Printf("Failed to delete bridge '%s': err='%s'", b.Name(), err.Error())
				return nil, grpc.Errorf(codes.Internal, "") // TODO #89
			}
		}
	}

	return &empty.Empty{}, nil
}

func (a VirtualMachineAgentAPI) BootVirtualMachineAgent(ctx context.Context, req *BootVirtualMachineAgentRequest) (*BootVirtualMachineAgentResponse, error) {
	id := uuid.NewV5(N0coreVirtualMachineNamespace, req.Name)
	q, err := qemu.OpenQemu(&id)
	if err != nil {
		return nil, grpc.Errorf(codes.Internal, "Failed to open qemu process: %s", err.Error())
	}
	if !q.IsRunning() {
		return nil, grpc.Errorf(codes.NotFound, "")
	}
	defer q.Close()

	if err := q.Boot(); err != nil {
		return nil, grpc.Errorf(codes.Internal, "Failed to boot qemu: %s", err.Error())
	}

	s, err := q.Status()
	if err != nil {
		return nil, grpc.Errorf(codes.Internal, "Failed to get qemu status: %s", err.Error())
	}

	return &BootVirtualMachineAgentResponse{
		State: GetAgentStateFromQemuState(s),
	}, nil
}

func (a VirtualMachineAgentAPI) RebootVirtualMachineAgent(ctx context.Context, req *RebootVirtualMachineAgentRequest) (*RebootVirtualMachineAgentResponse, error) {
	id := uuid.NewV5(N0coreVirtualMachineNamespace, req.Name)
	q, err := qemu.OpenQemu(&id)
	if err != nil {
		return nil, grpc.Errorf(codes.Internal, "Failed to open qemu process: %s", err.Error())
	}
	if !q.IsRunning() {
		return nil, grpc.Errorf(codes.NotFound, "")
	}
	defer q.Close()

	if req.Hard {
		if err := q.HardReset(); err != nil {
			return nil, grpc.Errorf(codes.Internal, "Failed to hard reboot qemu: %s", err.Error())
		}
	} else {
		return nil, grpc.Errorf(codes.Unimplemented, "reboot is unimplemented")
	}

	s, err := q.Status()
	if err != nil {
		return nil, grpc.Errorf(codes.Internal, "Failed to get qemu status: %s", err.Error())
	}

	return &RebootVirtualMachineAgentResponse{
		State: GetAgentStateFromQemuState(s),
	}, nil
}

func (a VirtualMachineAgentAPI) ShutdownVirtualMachineAgent(ctx context.Context, req *ShutdownVirtualMachineAgentRequest) (*ShutdownVirtualMachineAgentResponse, error) {
	id := uuid.NewV5(N0coreVirtualMachineNamespace, req.Name)
	q, err := qemu.OpenQemu(&id)
	if err != nil {
		return nil, grpc.Errorf(codes.Internal, "Failed to open qemu process: %s", err.Error())
	}
	if !q.IsRunning() {
		return nil, grpc.Errorf(codes.NotFound, "")
	}
	defer q.Close()

	if req.Hard {
		if err := q.HardShutdown(); err != nil {
			return nil, grpc.Errorf(codes.Internal, "Failed to hard shutdown qemu: %s", err.Error())
		}
	} else {
		if err := q.Shutdown(); err != nil {
			return nil, grpc.Errorf(codes.Internal, "Failed to shutdown qemu: %s", err.Error())
		}
	}

	s, err := q.Status()
	if err != nil {
		return nil, grpc.Errorf(codes.Internal, "Failed to get qemu status: %s", err.Error())
	}

	return &ShutdownVirtualMachineAgentResponse{
		State: GetAgentStateFromQemuState(s),
	}, nil
}
<|endoftext|>


<commit_before>/*
Copyright 2017 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 hosts

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"sync"

	"k8s.io/klog"
)

const (
	GUARD_BEGIN = "# Begin host entries managed by kops - do not edit"
	GUARD_END   = "# End host entries managed by kops"
)

var hostsFileMutex sync.Mutex

func UpdateHostsFileWithRecords(p string, addrToHosts map[string][]string) error {
	// For safety / sanity, we avoid concurrent updates from one process
	hostsFileMutex.Lock()
	defer hostsFileMutex.Unlock()

	stat, err := os.Stat(p)
	if err != nil {
		return fmt.Errorf("error getting file status of %q: %v", p, err)
	}

	data, err := ioutil.ReadFile(p)
	if err != nil {
		return fmt.Errorf("error reading file %q: %v", p, err)
	}

	var out []string
	inGuardBlock := false
	for _, line := range strings.Split(string(data), "\n") {
		k := strings.TrimSpace(line)
		if k == GUARD_BEGIN {
			if inGuardBlock {
				klog.Warningf("/etc/hosts guard-block begin seen while in guard block; will ignore")
			}
			inGuardBlock = true
		}

		if !inGuardBlock {
			out = append(out, line)
		}

		if k == GUARD_END {
			if !inGuardBlock {
				klog.Warningf("/etc/hosts guard-block end seen before guard-block start; will ignore end")
				// Don't output the line
				out = out[:len(out)-1]
			}

			inGuardBlock = false
		}
	}

	// Ensure a single blank line
	for {
		if len(out) == 0 {
			break
		}

		if out[len(out)-1] != "" {
			break
		}

		out = out[:len(out)-1]
	}
	out = append(out, "")

	var block []string
	for addr, hosts := range addrToHosts {
		sort.Strings(hosts)
		block = append(block, addr+"\t"+strings.Join(hosts, " "))
	}
	// Sort into a consistent order to minimize updates
	sort.Strings(block)

	out = append(out, GUARD_BEGIN)
	out = append(out, block...)
	out = append(out, GUARD_END)
	out = append(out, "")

	updated := []byte(strings.Join(out, "\n"))

	if bytes.Equal(updated, data) {
		klog.V(2).Infof("skipping update of unchanged /etc/hosts")
		return nil
	}

	// Note that because we are bind mounting /etc/hosts, we can't do a normal atomic file write
	// (where we write a temp file and rename it)
	// TODO: We should just hold the file open while we read & write it
	err = ioutil.WriteFile(p, updated, stat.Mode().Perm())
	if err != nil {
		return fmt.Errorf("error writing file %q: %v", p, err)
	}

	return nil
}

func atomicWriteFile(filename string, data []byte, perm os.FileMode) error {
	dir := filepath.Dir(filename)

	tempFile, err := ioutil.TempFile(dir, ".tmp"+filepath.Base(filename))
	if err != nil {
		return fmt.Errorf("error creating temp file in %q: %v", dir, err)
	}

	mustClose := true
	mustRemove := true

	defer func() {
		if mustClose {
			if err := tempFile.Close(); err != nil {
				klog.Warningf("error closing temp file: %v", err)
			}
		}

		if mustRemove {
			if err := os.Remove(tempFile.Name()); err != nil {
				klog.Warningf("error removing temp file %q: %v", tempFile.Name(), err)
			}
		}
	}()

	if _, err := tempFile.Write(data); err != nil {
		return fmt.Errorf("error writing temp file: %v", err)
	}

	if err := tempFile.Close(); err != nil {
		return fmt.Errorf("error closing temp file: %v", err)
	}

	mustClose = false

	if err := os.Chmod(tempFile.Name(), perm); err != nil {
		return fmt.Errorf("error changing mode of temp file: %v", err)
	}

	if err := os.Rename(tempFile.Name(), filename); err != nil {
		return fmt.Errorf("error moving temp file %q to %q: %v", tempFile.Name(), filename, err)
	}

	mustRemove = false
	return nil
}
<commit_msg>Avoid concurrent write corruption to /etc/hosts<commit_after>/*
Copyright 2017 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 hosts

import (
	"bytes"
	"fmt"
	"io/ioutil"
	math_rand "math/rand"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"sync"
	"time"

	"k8s.io/klog"
)

const (
	GUARD_BEGIN = "# Begin host entries managed by kops - do not edit"
	GUARD_END   = "# End host entries managed by kops"
)

var hostsFileMutex sync.Mutex

func UpdateHostsFileWithRecords(p string, addrToHosts map[string][]string) error {
	// For safety / sanity, we avoid concurrent updates from one process
	hostsFileMutex.Lock()
	defer hostsFileMutex.Unlock()

	stat, err := os.Stat(p)
	if err != nil {
		return fmt.Errorf("error getting file status of %q: %v", p, err)
	}

	data, err := ioutil.ReadFile(p)
	if err != nil {
		return fmt.Errorf("error reading file %q: %v", p, err)
	}

	var out []string
	inGuardBlock := false
	for _, line := range strings.Split(string(data), "\n") {
		k := strings.TrimSpace(line)
		if k == GUARD_BEGIN {
			if inGuardBlock {
				klog.Warningf("/etc/hosts guard-block begin seen while in guard block; will ignore")
			}
			inGuardBlock = true
		}

		if !inGuardBlock {
			out = append(out, line)
		}

		if k == GUARD_END {
			if !inGuardBlock {
				klog.Warningf("/etc/hosts guard-block end seen before guard-block start; will ignore end")
				// Don't output the line
				out = out[:len(out)-1]
			}

			inGuardBlock = false
		}
	}

	// Ensure a single blank line
	for {
		if len(out) == 0 {
			break
		}

		if out[len(out)-1] != "" {
			break
		}

		out = out[:len(out)-1]
	}
	out = append(out, "")

	var block []string
	for addr, hosts := range addrToHosts {
		sort.Strings(hosts)
		block = append(block, addr+"\t"+strings.Join(hosts, " "))
	}
	// Sort into a consistent order to minimize updates
	sort.Strings(block)

	out = append(out, GUARD_BEGIN)
	out = append(out, block...)
	out = append(out, GUARD_END)
	out = append(out, "")

	updated := []byte(strings.Join(out, "\n"))

	if bytes.Equal(updated, data) {
		klog.V(2).Infof("skipping update of unchanged /etc/hosts")
		return nil
	}

	// Note that because we are bind mounting /etc/hosts, we can't do a normal atomic file write
	// (where we write a temp file and rename it)
	if err := pseudoAtomicWrite(p, updated, stat.Mode()); err != nil {
		return fmt.Errorf("error writing file %q: %v", p, err)
	}

	return nil
}

// Because we are bind-mounting /etc/hosts, we can't do a normal
// atomic file write (where we write a temp file and rename it);
// instead we write the file, pause, re-read and see if anyone else
// wrote in the meantime; if so we rewrite again.  By pausing for a
// random amount of time, eventually we'll win the write race and
// exit.  This doesn't guarantee fairness, but it should mean that the
// end-result is not malformed (i.e. partial writes).
func pseudoAtomicWrite(p string, b []byte, mode os.FileMode) error {
	attempt := 0
	for {
		attempt++
		if attempt > 10 {
			return fmt.Errorf("failed to consistently write file %q - too many retries", p)
		}

		if err := ioutil.WriteFile(p, b, mode); err != nil {
			klog.Warningf("error writing file %q: %v", p, err)
			continue
		}

		n := 1 + math_rand.Intn(20)
		time.Sleep(time.Duration(n) * time.Millisecond)

		contents, err := ioutil.ReadFile(p)
		if err != nil {
			klog.Warningf("error re-reading file %q: %v", p, err)
			continue
		}

		if bytes.Equal(contents, b) {
			return nil
		}

		klog.Warningf("detected concurrent write to file %q, will retry", p)
	}
}

func atomicWriteFile(filename string, data []byte, perm os.FileMode) error {
	dir := filepath.Dir(filename)

	tempFile, err := ioutil.TempFile(dir, ".tmp"+filepath.Base(filename))
	if err != nil {
		return fmt.Errorf("error creating temp file in %q: %v", dir, err)
	}

	mustClose := true
	mustRemove := true

	defer func() {
		if mustClose {
			if err := tempFile.Close(); err != nil {
				klog.Warningf("error closing temp file: %v", err)
			}
		}

		if mustRemove {
			if err := os.Remove(tempFile.Name()); err != nil {
				klog.Warningf("error removing temp file %q: %v", tempFile.Name(), err)
			}
		}
	}()

	if _, err := tempFile.Write(data); err != nil {
		return fmt.Errorf("error writing temp file: %v", err)
	}

	if err := tempFile.Close(); err != nil {
		return fmt.Errorf("error closing temp file: %v", err)
	}

	mustClose = false

	if err := os.Chmod(tempFile.Name(), perm); err != nil {
		return fmt.Errorf("error changing mode of temp file: %v", err)
	}

	if err := os.Rename(tempFile.Name(), filename); err != nil {
		return fmt.Errorf("error moving temp file %q to %q: %v", tempFile.Name(), filename, err)
	}

	mustRemove = false
	return nil
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package simplepush

import (
	"crypto/rand"
	"encoding/json"
	"fmt"
	"net/http"
	"net/http/httptest"
	"net/url"
	"strconv"
	"testing"
	"time"
)

func newTestHandler(tb TBLoggingInterface) *Application {

	tlogger, _ := NewLogger(&TestLogger{DEBUG, tb})

	mx := &TestMetrics{}
	mx.Init(nil, nil)
	store := &NoStore{logger: tlogger, maxChannels: 10}
	pping := &NoopPing{}
	app := NewApplication()
	app.hostname = "test"
	app.host = "test"
	app.clientMinPing = 10 * time.Second
	app.clientHelloTimeout = 10 * time.Second
	app.pushLongPongs = true
	app.metrics = mx
	app.store = store
	app.propping = pping
	app.SetLogger(tlogger)
	locator := &NoLocator{logger: tlogger}
	router := NewBroadcastRouter()
	router.Init(app, router.ConfigStruct())
	app.SetRouter(router)
	app.SetLocator(locator)

	eh := NewEndpointHandler()
	ehConfig := eh.ConfigStruct()
	ehConfig.(*EndpointHandlerConfig).MaxDataLen = 140
	if err := eh.Init(app, ehConfig); err != nil {
		tb.Logf("Failed to create endpoint: %s", err)
		return nil
	}
	app.SetEndpointHandler(eh)

	return app
}

func randomText(size int) string {
	rbuf := make([]byte, size)
	rand.Read(rbuf)
	for i := 0; i < len(rbuf); i++ {
		rbuf[i] = uint8(rbuf[i])%94 + 32
	}
	return string(rbuf)
}

func Benchmark_UpdateHandler(b *testing.B) {
	uaid := "deadbeef000000000000000000000000"
	chid := "decafbad000000000000000000000000"
	app := newTestHandler(b)
	defer app.Close()
	if app == nil {
		b.Fatal()
	}
	worker := &NoWorker{Logger: app.Logger()}
	worker.SetUAID(uaid)
	app.AddWorker(uaid, worker)
	resp := httptest.NewRecorder()
	key, _ := app.Store().IDsToKey(uaid, chid)
	updateUrl := fmt.Sprintf("http://test/update/%s", key)
	tmux := app.EndpointHandler().ServeMux()
	vals := make(url.Values)
	data := randomText(64)
	// Begin benchmark:
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		req, err := http.NewRequest("PUT", updateUrl, nil)
		if req == nil || err != nil {
			b.Errorf("PUT failed: %s", err)
			b.Fail()
		}
		req.Form = vals
		req.Form.Set("version", strconv.FormatInt(int64(i), 10))
		req.Form.Set("data", data)
		tmux.ServeHTTP(resp, req)
		if resp.Body.String() != "{}" {
			b.Errorf(`Unexpected response from server: "%s"`, resp.Body.String())
			b.Fail()
		}
		resp.Body.Reset()
	}
}

func Test_UpdateHandler(t *testing.T) {
	var err error
	uaid := "deadbeef000000000000000000000000"
	chid := "decafbad000000000000000000000000"
	data := "This is a test of the emergency broadcasting system."

	app := newTestHandler(t)
	defer app.Close()
	worker := &NoWorker{Logger: app.Logger()}
	worker.SetUAID(uaid)
	app.AddWorker(uaid, worker)
	resp := httptest.NewRecorder()
	// don't bother with encryption right now.
	key, _ := app.Store().IDsToKey(uaid, chid)
	req, err := http.NewRequest("PUT",
		fmt.Sprintf("http://test/update/%s", key),
		nil)
	if req == nil {
		t.Fatal("Update put returned nil")
	}
	if err != nil {
		t.Fatal(err)
	}
	req.Form = make(url.Values)
	req.Form.Add("version", "1")
	req.Form.Add("data", data)

	// Yay! Actually try the test!
	tmux := app.EndpointHandler().ServeMux()
	tmux.ServeHTTP(resp, req)
	if resp.Body.String() != "{}" {
		t.Error("Unexpected response from server")
	}
	rep := SendData{}
	if err = json.Unmarshal(worker.Outbuffer, &rep); err != nil {
		t.Errorf("Could not read output buffer %s", err.Error())
	}
	if rep.Data != data {
		t.Error("Returned data does not match expected value")
	}
	if rep.Version != 1 {
		t.Error("Returned version does not match expected value")
	}

	//retry without a Content-Type header
	resp = httptest.NewRecorder()
	req.Header.Set("Content-Type", "")
	tmux.ServeHTTP(resp, req)
	if resp.Body.String() != "{}" {
		t.Error("Unexpected response from server")
	}
	rep = SendData{}
	if err = json.Unmarshal(worker.Outbuffer, &rep); err != nil {
		t.Errorf("Could not read output buffer %s", err.Error())
	}
	if rep.Data != data {
		t.Error("Returned data does not match expected value")
	}
	if rep.Version != 1 {
		t.Error("Returned version does not match expected value")
	}

}
<commit_msg>Use ephemeral ports in the update handler test.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package simplepush

import (
	"crypto/rand"
	"encoding/json"
	"fmt"
	"net/http"
	"net/http/httptest"
	"net/url"
	"strconv"
	"testing"
	"time"
)

func newTestHandler(tb TBLoggingInterface) *Application {

	tlogger, _ := NewLogger(&TestLogger{DEBUG, tb})

	mx := &TestMetrics{}
	mx.Init(nil, nil)
	store := &NoStore{logger: tlogger, maxChannels: 10}
	pping := &NoopPing{}
	app := NewApplication()
	app.hostname = "test"
	app.host = "test"
	app.clientMinPing = 10 * time.Second
	app.clientHelloTimeout = 10 * time.Second
	app.pushLongPongs = true
	app.metrics = mx
	app.store = store
	app.propping = pping
	app.SetLogger(tlogger)
	locator := &NoLocator{logger: tlogger}
	router := NewBroadcastRouter()
	routerConf := router.ConfigStruct().(*BroadcastRouterConfig)
	routerConf.Listener.Addr = ""
	router.Init(app, routerConf)
	app.SetRouter(router)
	app.SetLocator(locator)

	sh := NewSocketHandler()
	shConfig := sh.ConfigStruct().(*SocketHandlerConfig)
	shConfig.Listener.Addr = ""
	if err := sh.Init(app, shConfig); err != nil {
		tb.Logf("Failed to create WebSocket handler: %s", err)
		return nil
	}
	app.SetSocketHandler(sh)

	eh := NewEndpointHandler()
	ehConfig := eh.ConfigStruct().(*EndpointHandlerConfig)
	ehConfig.Listener.Addr = ""
	ehConfig.MaxDataLen = 140
	if err := eh.Init(app, ehConfig); err != nil {
		tb.Logf("Failed to create update handler: %s", err)
		return nil
	}
	app.SetEndpointHandler(eh)

	return app
}

func randomText(size int) string {
	rbuf := make([]byte, size)
	rand.Read(rbuf)
	for i := 0; i < len(rbuf); i++ {
		rbuf[i] = uint8(rbuf[i])%94 + 32
	}
	return string(rbuf)
}

func Benchmark_UpdateHandler(b *testing.B) {
	uaid := "deadbeef000000000000000000000000"
	chid := "decafbad000000000000000000000000"
	app := newTestHandler(b)
	defer app.Close()
	if app == nil {
		b.Fatal()
	}
	worker := &NoWorker{Logger: app.Logger()}
	worker.SetUAID(uaid)
	app.AddWorker(uaid, worker)
	resp := httptest.NewRecorder()
	key, _ := app.Store().IDsToKey(uaid, chid)
	updateUrl := fmt.Sprintf("http://test/update/%s", key)
	tmux := app.EndpointHandler().ServeMux()
	vals := make(url.Values)
	data := randomText(64)
	// Begin benchmark:
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		req, err := http.NewRequest("PUT", updateUrl, nil)
		if req == nil || err != nil {
			b.Errorf("PUT failed: %s", err)
			b.Fail()
		}
		req.Form = vals
		req.Form.Set("version", strconv.FormatInt(int64(i), 10))
		req.Form.Set("data", data)
		tmux.ServeHTTP(resp, req)
		if resp.Body.String() != "{}" {
			b.Errorf(`Unexpected response from server: "%s"`, resp.Body.String())
			b.Fail()
		}
		resp.Body.Reset()
	}
}

func Test_UpdateHandler(t *testing.T) {
	var err error
	uaid := "deadbeef000000000000000000000000"
	chid := "decafbad000000000000000000000000"
	data := "This is a test of the emergency broadcasting system."

	app := newTestHandler(t)
	defer app.Close()
	worker := &NoWorker{Logger: app.Logger()}
	worker.SetUAID(uaid)
	app.AddWorker(uaid, worker)
	resp := httptest.NewRecorder()
	// don't bother with encryption right now.
	key, _ := app.Store().IDsToKey(uaid, chid)
	req, err := http.NewRequest("PUT",
		fmt.Sprintf("http://test/update/%s", key),
		nil)
	if req == nil {
		t.Fatal("Update put returned nil")
	}
	if err != nil {
		t.Fatal(err)
	}
	req.Form = make(url.Values)
	req.Form.Add("version", "1")
	req.Form.Add("data", data)

	// Yay! Actually try the test!
	tmux := app.EndpointHandler().ServeMux()
	tmux.ServeHTTP(resp, req)
	if resp.Body.String() != "{}" {
		t.Error("Unexpected response from server")
	}
	rep := SendData{}
	if err = json.Unmarshal(worker.Outbuffer, &rep); err != nil {
		t.Errorf("Could not read output buffer %s", err.Error())
	}
	if rep.Data != data {
		t.Error("Returned data does not match expected value")
	}
	if rep.Version != 1 {
		t.Error("Returned version does not match expected value")
	}

	//retry without a Content-Type header
	resp = httptest.NewRecorder()
	req.Header.Set("Content-Type", "")
	tmux.ServeHTTP(resp, req)
	if resp.Body.String() != "{}" {
		t.Error("Unexpected response from server")
	}
	rep = SendData{}
	if err = json.Unmarshal(worker.Outbuffer, &rep); err != nil {
		t.Errorf("Could not read output buffer %s", err.Error())
	}
	if rep.Data != data {
		t.Error("Returned data does not match expected value")
	}
	if rep.Version != 1 {
		t.Error("Returned version does not match expected value")
	}

}
<|endoftext|>


<commit_before>package main

import "testing"
import "runtime"
import "strconv"
import "os"
import "time"
import "fmt"
import "math/rand"

const BINARY_PORTS = {":9021", ":9022", ":9023", ":9024", ":9025"}
var ADDRS = []string {"128.52.161.243", "128.52.161.243", "128.52.161.243", "128.52.161.243", "128.52.161.243"}
var ADDRS_WITH_PORTS = []string {"128.52.161.243:9021", "128.52.161.243:9022", "128.52.161.243:9023",
  "128.52.161.243:9024", "128.52.161.243:9025"}
var PG_PORTS = []string {"5434", "5435", "5436", "5437", "5438"}
var SP_PORTS = []string {":9011", ":9012", ":9013", ":9014", ":9015"}

func StartServer(servers []string, me int) {
  pbinary_protocol_factory := thrift.NewTBinaryProtocolFactoryDefault()
  json_protocol_factory := thrift.NewTJSONProtocolFactory()
  transport_factory := thrift.NewTTransportFactory()

  binary_transport, err := thrift.NewTServerSocket(ADDRS[me] + PORT_BINARY[me])

  if err != nil {
    fmt.Println("Error opening socket: ", err)
    return
  }

  handler := NewBaristaHandler(ADDRS, me, PG_PORTS, SP_PORTS)
  processor := barista.NewBaristaProcessor(handler)
  binary_server := thrift.NewTSimpleServer4(processor, binary_transport, transport_factory, binary_protocol_factory)
  json_server := thrift.NewTSimpleServer4(processor, json_transport, transport_factory, json_protocol_factory)

  fmt.Println("Starting the Barista server (Binary Mode) on ", ADDRS[me] + BINARY_PORTS[me])
  go binary_server.Serve()
}

func TestBasic(t *testing.T) {
  const nservers = 5

  // store the addresses of the servers
  var kvh []string = make([]string, nservers)
  // initialize the addresses

  var kva []*TSimpleServer = make([]*TSimpleServer, nservers)

  for i := 0; i < nservers; i++ {
  	// start servers on the various machines
    kva[i] = StartServer(kvh, i)
  }

  ck := MakeClerk(kvh)
  var cka [nservers]*Clerk
  for i := 0; i < nservers; i++ {
    cka[i] = MakeClerk() // this no longer stores all the servers
  }

  fmt.Printf("Test: Basic open/execute/close ...\n")

  con, err := ck.OpenConnection(ADDRS_WITH_PORTS)
  if err != nil {
  	t.Fatalf("Error opening connection:", err)
  } else if con == nil {
  	t.Fatalf("Error nil connection returned by open:", err)
  }

  _, err = clerk.ExecuteSQL(ADDRS_WITH_PORTS con,
      "CREATE TABLE IF NOT EXISTS sqlpaxos_test (key text, value text)", nil)
  if err != nil {
    t.Fatalf("Error creating table:", err)
    return
  }

  // count number of rows in the table
  res, err := ck.ExecuteSQL(ADDRS_WITH_PORTS, con,
  	"select count(*) from sqlpaxos_test", nil)
  if err != nil || res == nil {
  	t.Fatalf("Error querying table:", err)
  }

  for _, tuple := range *(res.Tuples) {
    for _, cell := range *(tuple.Cells) {
      if "0" != cell {
      	t.Fatalf("Table should be empty: %s", cell)
      }
    }
  }

  // insert item into table
  key := "a"
  val := "100"
  res, err := ck.ExecuteSQL(ADDRS_WITH_PORTS,con,
  	"INSERT INTO sqlpaxos_test VALUES (\'"+ key +"\', \'" +
  		value +"\')", nil)
  if err != nil || res == nil {
  	t.Fatalf("Error querying table:", err)
  }

  // retrieve old item from table
  res, err := ck.ExecuteSQL(con,
  	"select value from sqlpaxos_test where key=\'" + key
  	  + "\'", nil)
  if err != nil || res == nil {
  	t.Fatalf("Error querying table:", err)
  }

  ck.Print_result_set(res)

  if res != nil && res.Tuples != nil {
    for _, tuple := range *(res.Tuples) {
      for _, cell := range *(tuple.Cells) {
        if val != cell {
          t.Fatalf("Table should be empty: %s", cell)
        }
      }
    }
  }

  err := ck.CloseConnection(ADDRS_WITH_PORTS)
	if err != nil {
  	t.Fatalf("Error closing connection:", err)
  }

  fmt.Printf("  ... Passed\n")
  return

  fmt.Printf("Test: Concurrent clients ...\n")

  for iters := 0; iters < 20; iters++ {
    const npara = 15
    var ca [npara]chan bool
    for nth := 0; nth < npara; nth++ {
      ca[nth] = make(chan bool)
      go func(me int) {
        defer func() { ca[me] <- true }()
        ci := (rand.Int() % nservers)
        myck := MakeClerk([]string{kvh[ci]})
        con, err := ck.OpenConnection()
		if err != nil {
		  t.Fatalf("Error opening connection:", err)
		} else if con == nil {
		  t.Fatalf("Error nil connection returned by open:", err)
		}
        if (rand.Int() % 1000) < 500 {
          // insert
          res, err := ck.ExecuteSQL(con,
  			"insert into sqlpaxos_test values (\'"+ key +"\', \'" +
  		    strconv.Itoa(rand.Int())) +"\')", nil)
		  if err != nil || res == nil {
		  	t.Fatalf("Error querying table:", err)
		  }
  		} else {
          // retrieve old item from table
		  res, err := ck.ExecuteSQL(con,
		  	"select value from sqlpaxos_test where key=\'" + key
		  	  + "\'", nil)
		  if err != nil || res == nil {
		  	t.Fatalf("Error querying table:", err)
		  }
        }
        err := ck.CloseConnection()
		if err != nil {
  		  t.Fatalf("Error closing connection:", err)
  		}
      }(nth)
    }

    for nth := 0; nth < npara; nth++ {
      <- ca[nth]
    }

    var va [nservers]string
    for i := 0; i < nservers; i++ {
	  res, err := ck.ExecuteSQL(con,
	  	"select value from sqlpaxos_test where key=\'" + key
	  	  + "\'", nil)
	  if err != nil || res == nil {
	  	t.Fatalf("Error querying table:", err)
	  }
	  for _, tuple := range *(res.Tuples) {
        for _, cell := range *(tuple.Cells) {
          va[i] = cell
    	}
      }

      if va[i] != va[0] {
        t.Fatalf("mismatch")
      }
    }
  }

  fmt.Printf("  ... Passed\n")

  time.Sleep(1 * time.Second)
}

<commit_msg>update test<commit_after>package main

import "testing"
import "runtime"
import "strconv"
import "os"
import "time"
import "fmt"
import "math/rand"

const BINARY_PORTS = []string {":9021", ":9022", ":9023", ":9024", ":9025"}
var ADDRS = []string {"128.52.161.243", "128.52.161.243", "128.52.161.243", "128.52.161.243", "128.52.161.243"}
var ADDRS_WITH_PORTS = []string {"128.52.161.243:9021", "128.52.161.243:9022", "128.52.161.243:9023",
  "128.52.161.243:9024", "128.52.161.243:9025"}
var PG_PORTS = []string {"5434", "5435", "5436", "5437", "5438"}
var SP_PORTS = []string {":9011", ":9012", ":9013", ":9014", ":9015"}

func StartServer(servers []string, me int) {
  pbinary_protocol_factory := thrift.NewTBinaryProtocolFactoryDefault()
  json_protocol_factory := thrift.NewTJSONProtocolFactory()
  transport_factory := thrift.NewTTransportFactory()

  binary_transport, err := thrift.NewTServerSocket(ADDRS[me] + PORT_BINARY[me])

  if err != nil {
    fmt.Println("Error opening socket: ", err)
    return
  }

  handler := NewBaristaHandler(ADDRS, me, PG_PORTS, SP_PORTS)
  processor := barista.NewBaristaProcessor(handler)
  binary_server := thrift.NewTSimpleServer4(processor, binary_transport, transport_factory, binary_protocol_factory)
  json_server := thrift.NewTSimpleServer4(processor, json_transport, transport_factory, json_protocol_factory)

  fmt.Println("Starting the Barista server (Binary Mode) on ", ADDRS[me] + BINARY_PORTS[me])
  go binary_server.Serve()
}

func TestBasic(t *testing.T) {
  const nservers = 5

  // store the addresses of the servers
  var kvh []string = make([]string, nservers)
  // initialize the addresses

  var kva []*TSimpleServer = make([]*TSimpleServer, nservers)

  for i := 0; i < nservers; i++ {
  	// start servers on the various machines
    kva[i] = StartServer(kvh, i)
  }

  ck := MakeClerk(kvh)
  var cka [nservers]*Clerk
  for i := 0; i < nservers; i++ {
    cka[i] = MakeClerk() // this no longer stores all the servers
  }

  fmt.Printf("Test: Basic open/execute/close ...\n")

  con, err := ck.OpenConnection(ADDRS_WITH_PORTS)
  if err != nil {
  	t.Fatalf("Error opening connection:", err)
  } else if con == nil {
  	t.Fatalf("Error nil connection returned by open:", err)
  }

  _, err = clerk.ExecuteSQL(ADDRS_WITH_PORTS, con,
      "CREATE TABLE IF NOT EXISTS sqlpaxos_test (key text, value text)", nil)
  if err != nil {
    t.Fatalf("Error creating table:", err)
    return
  }

  // count number of rows in the table
  res, err := ck.ExecuteSQL(ADDRS_WITH_PORTS, con,
  	"select count(*) from sqlpaxos_test", nil)
  if err != nil || res == nil {
  	t.Fatalf("Error querying table:", err)
  }

  for _, tuple := range *(res.Tuples) {
    for _, cell := range *(tuple.Cells) {
      if "0" != cell {
      	t.Fatalf("Table should be empty: %s", cell)
      }
    }
  }

  // insert item into table
  key := "a"
  val := "100"
  res, err := ck.ExecuteSQL(ADDRS_WITH_PORTS, con,
  	"INSERT INTO sqlpaxos_test VALUES ('"+ key +"', '" +
  		value +"')", nil)
  if err != nil || res == nil {
  	t.Fatalf("Error querying table:", err)
  }

  // retrieve old item from table
  res, err := ck.ExecuteSQL(con,
  	"select value from sqlpaxos_test where key='" + key
  	  + "'", nil)
  if err != nil || res == nil {
  	t.Fatalf("Error querying table:", err)
  }

  ck.Print_result_set(res)

  if res != nil && res.Tuples != nil {
    for _, tuple := range *(res.Tuples) {
      for _, cell := range *(tuple.Cells) {
        if val != cell {
          t.Fatalf("Table should be empty: %s", cell)
        }
      }
    }
  }

  err := ck.CloseConnection(ADDRS_WITH_PORTS)
	if err != nil {
  	t.Fatalf("Error closing connection:", err)
  }

  fmt.Printf("  ... Passed\n")
  return

  fmt.Printf("Test: Concurrent clients ...\n")

  for iters := 0; iters < 20; iters++ {
    const npara = 15
    var ca [npara]chan bool
    for nth := 0; nth < npara; nth++ {
      ca[nth] = make(chan bool)
      go func(me int) {
        defer func() { ca[me] <- true }()
        ci := (rand.Int() % nservers)
        myck := MakeClerk([]string{kvh[ci]})
        con, err := ck.OpenConnection()
		if err != nil {
		  t.Fatalf("Error opening connection:", err)
		} else if con == nil {
		  t.Fatalf("Error nil connection returned by open:", err)
		}
        if (rand.Int() % 1000) < 500 {
          // insert
          res, err := ck.ExecuteSQL(con,
  			"insert into sqlpaxos_test values ('"+ key +"', \'" +
  		    strconv.Itoa(rand.Int())) +"')", nil)
		  if err != nil || res == nil {
		  	t.Fatalf("Error querying table:", err)
		  }
  		} else {
          // retrieve old item from table
		  res, err := ck.ExecuteSQL(con,
		  	"select value from sqlpaxos_test where key='" + key
		  	  + "'", nil)
		  if err != nil || res == nil {
		  	t.Fatalf("Error querying table:", err)
		  }
        }
        err := ck.CloseConnection()
		if err != nil {
  		  t.Fatalf("Error closing connection:", err)
  		}
      }(nth)
    }

    for nth := 0; nth < npara; nth++ {
      <- ca[nth]
    }

    var va [nservers]string
    for i := 0; i < nservers; i++ {
	  res, err := ck.ExecuteSQL(con,
	  	"select value from sqlpaxos_test where key='" + key
	  	  + "'", nil)
	  if err != nil || res == nil {
	  	t.Fatalf("Error querying table:", err)
	  }
	  for _, tuple := range *(res.Tuples) {
        for _, cell := range *(tuple.Cells) {
          va[i] = cell
    	}
      }

      if va[i] != va[0] {
        t.Fatalf("mismatch")
      }
    }
  }

  fmt.Printf("  ... Passed\n")

  time.Sleep(1 * time.Second)
}

<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"path/filepath"

	"github.com/alecthomas/kingpin"
)

const (
	defaultAddress = "localhost:12003"
	defaultTimeout = "30"
)

var (
	version   string
	buildDate string
	buildHash string

	upstreamAddress string
	upstreamTimeout int
)

func main() {
	app := kingpin.New(filepath.Base(os.Args[0]), "Facette control utility.")
	app.HelpFlag.Short('h')

	// Global
	flagAddress := app.Flag("address", "Set upstream socket address.").Short('a').Default(defaultAddress).String()
	flagTimeout := app.Flag("timeout", "Set upstream connection timeout.").Short('t').Default(defaultTimeout).Int()

	// Version
	version := app.Command("version", "Display version and support information.")

	// Backend
	library := app.Command("library", "Manage library operations.")

	libraryDump := library.Command("dump", "Dump data from library.")
	libraryDumpOutput := libraryDump.Flag("output", "Set dump output file path.").Short('o').String()

	libraryRestore := library.Command("restore", "Restore data from dump into library.")
	libraryRestoreInput := libraryRestore.Flag("input", "Set dump input file path.").Short('i').Required().String()
	libraryRestoreMerge := libraryRestore.Flag("merge", "Merge data with existing library.").Short('m').Bool()

	// Parse command-line
	command := kingpin.MustParse(app.Parse(os.Args[1:]))

	upstreamAddress = *flagAddress
	upstreamTimeout = *flagTimeout

	switch command {
	case version.FullCommand():
		execVersion()

	case libraryDump.FullCommand():
		execBackupDump(*libraryDumpOutput)

	case libraryRestore.FullCommand():
		execBackupRestore(*libraryRestoreInput, *libraryRestoreMerge)
	}
}

func die(format string, v ...interface{}) {
	printError(format, v...)
	os.Exit(1)
}

func printError(format string, v ...interface{}) {
	fmt.Fprintf(os.Stderr, "Error: %s\n", fmt.Sprintf(format, v...))
}
<commit_msg>Fix facettectl default API address<commit_after>package main

import (
	"fmt"
	"os"
	"path/filepath"

	"github.com/alecthomas/kingpin"
)

const (
	defaultAddress = "http://localhost:12003"
	defaultTimeout = "30"
)

var (
	version   string
	buildDate string
	buildHash string

	upstreamAddress string
	upstreamTimeout int
)

func main() {
	app := kingpin.New(filepath.Base(os.Args[0]), "Facette control utility.")
	app.HelpFlag.Short('h')

	// Global
	flagAddress := app.Flag("address", "Set upstream socket address.").Short('a').Default(defaultAddress).String()
	flagTimeout := app.Flag("timeout", "Set upstream connection timeout.").Short('t').Default(defaultTimeout).Int()

	// Version
	version := app.Command("version", "Display version and support information.")

	// Backend
	library := app.Command("library", "Manage library operations.")

	libraryDump := library.Command("dump", "Dump data from library.")
	libraryDumpOutput := libraryDump.Flag("output", "Set dump output file path.").Short('o').String()

	libraryRestore := library.Command("restore", "Restore data from dump into library.")
	libraryRestoreInput := libraryRestore.Flag("input", "Set dump input file path.").Short('i').Required().String()
	libraryRestoreMerge := libraryRestore.Flag("merge", "Merge data with existing library.").Short('m').Bool()

	// Parse command-line
	command := kingpin.MustParse(app.Parse(os.Args[1:]))

	upstreamAddress = *flagAddress
	upstreamTimeout = *flagTimeout

	switch command {
	case version.FullCommand():
		execVersion()

	case libraryDump.FullCommand():
		execBackupDump(*libraryDumpOutput)

	case libraryRestore.FullCommand():
		execBackupRestore(*libraryRestoreInput, *libraryRestoreMerge)
	}
}

func die(format string, v ...interface{}) {
	printError(format, v...)
	os.Exit(1)
}

func printError(format string, v ...interface{}) {
	fmt.Fprintf(os.Stderr, "Error: %s\n", fmt.Sprintf(format, v...))
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"flag"
	"fmt"
	"io/ioutil"
	"launchpad.net/goyaml"
	"os"
	"text/template"
)

type Task struct {
	Name       string
	Parameters map[string]string
}

type Node struct {
	Name  string
	Tests []Task
	Steps []Task
}

type TaskResult struct {
	Success bool
	Message string
}

var boxkitePath string

func loadNode(path string) Node {
	file, e := ioutil.ReadFile(path)
	if e != nil {
		fmt.Printf("File error: %v\n", e)
		os.Exit(1)
	}

	var n Node

	err := goyaml.Unmarshal(file, &n)
	if err != nil {
		fmt.Printf("YAML error: %v\n", err)
		os.Exit(1)
	}
	return n
}

func templatize(s string, p map[string]string) string {
	var result bytes.Buffer

	template, err := template.New("result").Parse(s)
	if err != nil {
		panic(err)
	}

	err = template.Execute(&result, p)
	if err != nil {
		panic(err)
	}
	return result.String()
}

func (t Task) doTask(params map[string]string) <-chan TaskResult {
	c := make(chan TaskResult)
	go func() {
		fmt.Println("In Task:", t.Name)
		fmt.Println("Params are:", params)

		if t.Name == "core.Exec" {
			command := templatize(t.Parameters["command"], params)

			result := fmt.Sprintf("core.Exec: %s", command)

			c <- TaskResult{true, result}
		} else {
			n := loadNode(fmt.Sprintf("%s/%s.yaml", boxkitePath, t.Name))

			for k, v := range t.Parameters {

				t.Parameters[k] = templatize(v, params)

			}

			tc := n.doNode(t.Parameters)

			result := <-tc
			if result.Success {
				fmt.Println("SUCCESS:", result.Message)
			} else {
				fmt.Println("FAILURE:", result.Message)
			}
			c <- result
		}
	}()
	return c
}

func (n Node) doNode(params map[string]string) <-chan TaskResult {

	c := make(chan TaskResult)

	go func() {

		fmt.Println("In Node:", n.Name)
		fmt.Println("Params are:", params)
		for _, test := range n.Tests {
			fmt.Println(n.Name, "- Test:", test.Name)
			tc := test.doTask(params)
			result := <-tc
			fmt.Println("--", result.Message)
		}

		for _, step := range n.Steps {
			fmt.Println(n.Name, "- Step:", step.Name)

			sc := step.doTask(params)
			result := <-sc
			fmt.Println("--", result.Message)

		}

		c <- TaskResult{true, "Hooray!"}
	}()
	return c
}

func main() {
	flag.StringVar(&boxkitePath, "b", "/etc/boxkite", "Directory where Boxkite files live")

	flag.Parse()

	// You must provide at least one boxkite file to start with
	if len(flag.Args()) != 1 {
		flag.Usage()
		os.Exit(1)
	}

	fmt.Println("Path is:", boxkitePath)
	fmt.Println("Root is:", flag.Arg(0))
	n := loadNode(flag.Arg(0))
	c := n.doNode(make(map[string]string))

	result := <-c

	if result.Success {
		fmt.Println("SUCCESS:", result.Message)
	} else {
		fmt.Println("FAILURE:", result.Message)
	}

}
<commit_msg>Now we exec programs<commit_after>package main

import (
	"bytes"
	"flag"
	"fmt"
	"io/ioutil"
	"launchpad.net/goyaml"
	"os"
	"os/exec"
	"text/template"
)

type Task struct {
	Name       string
	Parameters map[string]string
	Args       []string
}

type Node struct {
	Name  string
	Tests []Task
	Steps []Task
}

type TaskResult struct {
	Success bool
	Message string
}

var boxkitePath string

func loadNode(path string) Node {
	file, e := ioutil.ReadFile(path)
	if e != nil {
		fmt.Printf("File error: %v\n", e)
		os.Exit(1)
	}

	var n Node

	err := goyaml.Unmarshal(file, &n)
	if err != nil {
		fmt.Printf("YAML error: %v\n", err)
		os.Exit(1)
	}
	return n
}

func templatize(s string, p map[string]string) string {
	var result bytes.Buffer

	template, err := template.New("result").Parse(s)
	if err != nil {
		panic(err)
	}

	err = template.Execute(&result, p)
	if err != nil {
		panic(err)
	}
	return result.String()
}

func (t Task) doTask(params map[string]string) <-chan TaskResult {
	c := make(chan TaskResult)
	go func() {
		fmt.Println("In Task:", t.Name)
		fmt.Println("Params are:", params)

		for i, arg := range t.Args {
			t.Args[i] = templatize(arg, params)
		}

		for k, v := range t.Parameters {
			t.Parameters[k] = templatize(v, params)
		}

		if t.Name == "core.Exec" {

			cmd := exec.Command(t.Args[0], t.Args[1:]...)
			cmdOut, err := cmd.Output()

			if err != nil {
				c <- TaskResult{false, string(cmdOut)}
			} else {
				c <- TaskResult{true, string(cmdOut)}
			}

		} else {
			n := loadNode(fmt.Sprintf("%s/%s.yaml", boxkitePath, t.Name))

			tc := n.doNode(t.Parameters)

			result := <-tc
			if result.Success {
				fmt.Println("SUCCESS:", result.Message)
			} else {
				fmt.Println("FAILURE:", result.Message)
			}
			c <- result
		}
	}()
	return c
}

func (n Node) doNode(params map[string]string) <-chan TaskResult {

	c := make(chan TaskResult)

	go func() {

		fmt.Println("In Node:", n.Name)
		fmt.Println("Params are:", params)
		for _, test := range n.Tests {
			fmt.Println(n.Name, "- Test:", test.Name)
			tc := test.doTask(params)
			result := <-tc
			fmt.Println("--", result.Message)
		}

		for _, step := range n.Steps {
			fmt.Println(n.Name, "- Step:", step.Name)

			sc := step.doTask(params)
			result := <-sc
			fmt.Println("--", result.Message)

		}

		c <- TaskResult{true, "Hooray!"}
	}()
	return c
}

func main() {
	flag.StringVar(&boxkitePath, "b", "/etc/boxkite", "Directory where Boxkite files live")

	flag.Parse()

	// You must provide at least one boxkite file to start with
	if len(flag.Args()) != 1 {
		flag.Usage()
		os.Exit(1)
	}

	fmt.Println("Path is:", boxkitePath)
	fmt.Println("Root is:", flag.Arg(0))
	n := loadNode(flag.Arg(0))
	c := n.doNode(make(map[string]string))

	result := <-c

	if result.Success {
		fmt.Println("SUCCESS:", result.Message)
	} else {
		fmt.Println("FAILURE:", result.Message)
	}

}
<|endoftext|>


<commit_before>package overcurrent

import (
	"errors"
	"math/rand"
	"time"
)

type CircuitState int
type CircuitEvent int

const (
	OpenState CircuitState = iota
	ClosedState
	HalfClosedState

	TripEvent CircuitEvent = iota
	ResetEvent
)

var (
	CircuitOpenError       = errors.New("Circuit is open.")
	InvocationTimeoutError = errors.New("Invocation has timed out.")
)

//
//

// A CircuitBreaker protects the invocation of a function and monitors failures.
// After a certain failure threshold is reached, future invocations will instead
// return a CircuitOpenError instead of attempting to invoke the function again.
type CircuitBreaker struct {
	config BreakerConfig

	// A channel on which trip and reset events are sent. These can be monitored
	// for logging purposes, or safely ignored.
	Events chan CircuitEvent

	hardTrip        bool
	lastFailureTime *time.Time
}

func NewBreaker(config BreakerConfig) *CircuitBreaker {
	return &CircuitBreaker{
		config: config,
		Events: make(chan CircuitEvent),
	}
}

func (cb *CircuitBreaker) State() CircuitState {
	if cb.config.TripCondition.ShouldTrip() {
		if time.Now().Sub(*cb.lastFailureTime) > time.Duration(cb.config.ResetTimeout) {
			return HalfClosedState
		} else {
			return OpenState
		}
	} else {
		return ClosedState
	}
}

// Attempt to call the given function if the circuit breaker is closed, or if
// the circuit breaker is half-closed (with some probability). Otherwise, return
// a CircuitOpenError. If the function times out, the circuit breaker will fail
// with a InvocationTimeoutError. If the function is invoked and yields an error
// value, that value is returned.
func (cb *CircuitBreaker) Call(f func() error) error {
	if !cb.shouldTry() {
		return CircuitOpenError
	}

	if err := cb.callWithTimeout(f); err != nil && cb.config.FailureInterpreter.ShouldTrip(err) {
		cb.recordError()
		return err
	}

	cb.Reset()
	return nil
}

func (cb *CircuitBreaker) shouldTry() bool {
	if cb.hardTrip || cb.State() == OpenState {
		return false
	}

	if cb.State() == HalfClosedState {
		return rand.Float64() <= cb.config.HalfClosedRetryProbability
	}

	return true
}

func (cb *CircuitBreaker) callWithTimeout(f func() error) error {
	if cb.config.InvocationTimeout == 0 {
		return f()
	}

	c := make(chan error)
	go func() {
		c <- f()
		close(c)
	}()

	select {
	case err := <-c:
		return err
	case <-time.After(time.Duration(cb.config.InvocationTimeout)):
		return InvocationTimeoutError
	}
}

func (cb *CircuitBreaker) recordError() {
	now := time.Now()
	cb.lastFailureTime = &now

	cb.config.TripCondition.Failure()
	cb.sendEvent(TripEvent)
}

func (cb *CircuitBreaker) sendEvent(event CircuitEvent) {
	select {
	case cb.Events <- event:
	}
}

func (cb *CircuitBreaker) Trip() {
	cb.hardTrip = true
	cb.recordError()
}

func (cb *CircuitBreaker) Reset() {
	cb.lastFailureTime = nil
	cb.hardTrip = false

	cb.config.TripCondition.Success()
	cb.sendEvent(ResetEvent)
}
<commit_msg>Reformat code.<commit_after>package overcurrent

import (
	"errors"
	"math/rand"
	"time"
)

type CircuitState int
type CircuitEvent int

const (
	OpenState CircuitState = iota
	ClosedState
	HalfClosedState

	TripEvent CircuitEvent = iota
	ResetEvent
)

var (
	CircuitOpenError       = errors.New("Circuit is open.")
	InvocationTimeoutError = errors.New("Invocation has timed out.")
)

//
//

// A CircuitBreaker protects the invocation of a function and monitors failures.
// After a certain failure threshold is reached, future invocations will instead
// return a CircuitOpenError instead of attempting to invoke the function again.
type CircuitBreaker struct {
	config BreakerConfig

	// A channel on which trip and reset events are sent. These can be monitored
	// for logging purposes, or safely ignored.
	Events chan CircuitEvent

	hardTrip        bool
	lastFailureTime *time.Time
}

func NewBreaker(config BreakerConfig) *CircuitBreaker {
	return &CircuitBreaker{
		config: config,
		Events: make(chan CircuitEvent),
	}
}

// Attempt to call the given function if the circuit breaker is closed, or if
// the circuit breaker is half-closed (with some probability). Otherwise, return
// a CircuitOpenError. If the function times out, the circuit breaker will fail
// with a InvocationTimeoutError. If the function is invoked and yields an error
// value, that value is returned.
func (cb *CircuitBreaker) Call(f func() error) error {
	if !cb.shouldTry() {
		return CircuitOpenError
	}

	if err := cb.callWithTimeout(f); err != nil && cb.config.FailureInterpreter.ShouldTrip(err) {
		cb.recordFailure()
		return err
	}

	cb.Reset()
	return nil
}

// Manually trip the circuit breaker. The circuit breaker will remain open until
// it is manually reset.
func (cb *CircuitBreaker) Trip() {
	cb.hardTrip = true
	cb.recordFailure()
}

// Reset the circuit breaker.
func (cb *CircuitBreaker) Reset() {
	cb.lastFailureTime = nil
	cb.hardTrip = false

	cb.config.TripCondition.Success()
	cb.sendEvent(ResetEvent)
}

func (cb *CircuitBreaker) shouldTry() bool {
	if cb.hardTrip || cb.state() == OpenState {
		return false
	}

	if cb.state() == HalfClosedState {
		return rand.Float64() <= cb.config.HalfClosedRetryProbability
	}

	return true
}

func (cb *CircuitBreaker) state() CircuitState {
	if cb.config.TripCondition.ShouldTrip() {
		if time.Now().Sub(*cb.lastFailureTime) > time.Duration(cb.config.ResetTimeout) {
			return HalfClosedState
		} else {
			return OpenState
		}
	} else {
		return ClosedState
	}
}

func (cb *CircuitBreaker) callWithTimeout(f func() error) error {
	if cb.config.InvocationTimeout == 0 {
		return f()
	}

	c := make(chan error)
	go func() {
		c <- f()
		close(c)
	}()

	select {
	case err := <-c:
		return err
	case <-time.After(time.Duration(cb.config.InvocationTimeout)):
		return InvocationTimeoutError
	}
}

func (cb *CircuitBreaker) recordFailure() {
	now := time.Now()
	cb.lastFailureTime = &now

	cb.config.TripCondition.Failure()
	cb.sendEvent(TripEvent)
}

func (cb *CircuitBreaker) sendEvent(event CircuitEvent) {
	select {
	case cb.Events <- event:
	}
}
<|endoftext|>


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

/*
bricker is a API for the Tinkerforge Hardware based on the brick daemon (brickd).

A bricker is a manager.
It uses one or more connectors to send and receive packets from brick daemons (real hardware).

The connectors could send and receive events to a specific address.
The packets will encapsulate in events.
The bricker is also a producer of events.
To use this events their will be need consumer (subscriber).

For using this API you need a running brick daemon (brickd) or some hardware with a brick daemon,
please use an actual version.
You get the daemon from http://www.tinkerforge.com/en/doc/Software/Brickd.html#brickd as
source or binary package.

This API based on the documentation of the TCP/IP API from http://www.tinkerforge.com/en/doc/index.html#/software-tcpip-open and so this documentation is also useful.
*/
package bricker

import (
	"github.com/dirkjabl/bricker/connector"
	"github.com/dirkjabl/bricker/event"
	"github.com/dirkjabl/bricker/util/hash"
)

// The bricker type.
// A bricker managed connectors and subscriber.
type Bricker struct {
	connection        map[string]connector.Connector
	first             string
	uids              map[uint32]string
	subscriber        map[hash.Hash]map[string]Subscriber
	choosers          []uint8
	defaultsubscriber Subscriber
}

// New create the bricker.
// The new bricker start direct the service.
// After start, the bricker has no connection and no subscriber.
func New() *Bricker {
	return &Bricker{
		connection: make(map[string]connector.Connector),
		first:      "",
		uids:       make(map[uint32]string),
		subscriber: make(map[hash.Hash]map[string]Subscriber),
		choosers:   make([]uint8, 0)}
}

// Done release all connections and subscriber and release all resources.
func (b *Bricker) Done() {
	// release all subscriber
	for _, subs := range b.subscriber {
		for _, s := range subs {
			b.Unsubscribe(s)
		}
	}
	// Stop all bricker
	for name, _ := range b.connection {
		b.Release(name)
	}
}

// Internal method: read wait for a new event and forward it to the dispatcher.
func (b *Bricker) read(c connector.Connector, n string) {
	var ev *event.Event
	for {
		ev = c.Receive()
		if ev == nil {
			return // done, no more packets
		}
		ev.ConnectorName = n
		go b.dispatch(ev)
	}
}

// Internal method: write takes a event and send it to the right bricker (dispatch).
func (b *Bricker) write(e *event.Event) {
	if e != nil {
		if conn, ok := b.connection[e.ConnectorName]; ok {
			conn.Send(e)
		} else {
			e.Err = NewError(ErrorConnectorNameNotExists)
			go b.dispatch(e)
		}
	}
}

// Internal method: process dispatch the event to the right subscriber.
func (b *Bricker) dispatch(e *event.Event) {
	var h hash.Hash
	if e.Packet == nil { // without a packet, no subscriber could be determined
		go b.process(e, b.defaultsubscriber)
	} else {
		for _, chooser := range b.choosers {
			h = hash.New(chooser, e.Packet.Head.Uid, e.Packet.Head.FunctionID)
			if s, ok := b.subscriber[h]; ok {
				go func(ev *event.Event, subs map[string]Subscriber) {
					for _, s := range subs {
						go b.process(e, s)
					}
				}(e, s)
			}
		}
	}
}

// Internal method: process notify given subscriber.
func (b *Bricker) process(e *event.Event, sub Subscriber) {
	if sub == nil {
		return // no subscriber, no notify
	} else {
		sub.Notify(e)
		if !sub.Subscription().Callback { // not a callback, call only once
			b.Unsubscribe(sub)
		}
	}
}
<commit_msg>Fallback default subscriber call implemented in the dispatcher routine.<commit_after>// Copyright 2014 Dirk Jablonowski. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

/*
bricker is a API for the Tinkerforge Hardware based on the brick daemon (brickd).

A bricker is a manager.
It uses one or more connectors to send and receive packets from brick daemons (real hardware).

The connectors could send and receive events to a specific address.
The packets will encapsulate in events.
The bricker is also a producer of events.
To use this events their will be need consumer (subscriber).
There is a fallback mechanism for events without a consumer (default fallback subscriber).

For using this API you need a running brick daemon (brickd) or some hardware with a brick daemon,
please use an actual version of the daemon.
You get the daemon from http://www.tinkerforge.com/en/doc/Software/Brickd.html#brickd as
source or binary package.

This API based on the documentation of the TCP/IP API from http://www.tinkerforge.com/en/doc/index.html#/software-tcpip-open and so this documentation is also useful.
*/
package bricker

import (
	"github.com/dirkjabl/bricker/connector"
	"github.com/dirkjabl/bricker/event"
	"github.com/dirkjabl/bricker/util/hash"
)

// The bricker type.
// A bricker managed connectors and subscriber.
type Bricker struct {
	connection        map[string]connector.Connector
	first             string
	uids              map[uint32]string
	subscriber        map[hash.Hash]map[string]Subscriber
	choosers          []uint8
	defaultsubscriber Subscriber
}

// New create the bricker.
// The new bricker start direct the service.
// After start, the bricker has no connection and no subscriber.
func New() *Bricker {
	return &Bricker{
		connection: make(map[string]connector.Connector),
		first:      "",
		uids:       make(map[uint32]string),
		subscriber: make(map[hash.Hash]map[string]Subscriber),
		choosers:   make([]uint8, 0)}
}

// Done release all connections and subscriber and release all resources.
func (b *Bricker) Done() {
	// release all subscriber
	for _, subs := range b.subscriber {
		for _, s := range subs {
			b.Unsubscribe(s)
		}
	}
	// Stop all bricker
	for name, _ := range b.connection {
		b.Release(name)
	}
}

// Internal method: read wait for a new event and forward it to the dispatcher.
func (b *Bricker) read(c connector.Connector, n string) {
	var ev *event.Event
	for {
		ev = c.Receive()
		if ev == nil {
			return // done, no more packets
		}
		ev.ConnectorName = n
		go b.dispatch(ev)
	}
}

// Internal method: write takes a event and send it to the right bricker (dispatch).
func (b *Bricker) write(e *event.Event) {
	if e != nil {
		if conn, ok := b.connection[e.ConnectorName]; ok {
			conn.Send(e)
		} else {
			e.Err = NewError(ErrorConnectorNameNotExists)
			go b.dispatch(e)
		}
	}
}

// Internal method: process dispatch the event to the right subscriber.
func (b *Bricker) dispatch(e *event.Event) {
	var h hash.Hash
	if e.Packet == nil { // without a packet, no subscriber could be determined
		go b.process(e, b.defaultsubscriber)
	} else {
		match := false
		for _, chooser := range b.choosers {
			h = hash.New(chooser, e.Packet.Head.Uid, e.Packet.Head.FunctionID)
			if s, ok := b.subscriber[h]; ok {
				match = true && (len(s) > 0)
				go func(ev *event.Event, subs map[string]Subscriber) {
					for _, s := range subs {
						go b.process(e, s)
					}
				}(e, s)
			}
		}
		if !match { // no subscriber hash matched against packet hash
			go b.process(e, b.defaultsubscriber)
		}
	}
}

// Internal method: process notify given subscriber.
func (b *Bricker) process(e *event.Event, sub Subscriber) {
	if sub == nil {
		return // no subscriber, no notify
	} else {
		sub.Notify(e)
		if !sub.Subscription().Callback { // not a callback, call only once
			b.Unsubscribe(sub)
		}
	}
}
<|endoftext|>