<commit_before>// Copyright 2015 TVersity Inc. All rights reserved.
// Use of this source code is governed by an Apache 2.0
// license that can be found in the LICENSE file.

package main

import (
	// #include <stdio.h>
	// #include <stdlib.h>
	// #include "callbacks.h"
	"C"

	"github.com/tversity/appflinger-go"
)
import (
	"fmt"
	"unsafe"
)

const (
	MockDuration = 60
)

// This struct will implement the appflinger.AppFlinger interface which is needed in order to
// receive the control channel commands and process them
type AppflingerListener struct {
	// C callback pointers
	// Note - we cannot invoke C function pointers from Go so we use a helper C function to do it
	// e.g. to invoke the on_ui_frame_cb function pointer we use C.invoke_on_ui_frame()
	cb *C.appflinger_callbacks_t
}

func NewAppflingerListener(cb *C.appflinger_callbacks_t) (self *AppflingerListener) {
	self = &AppflingerListener{}
	self.cb = cb
	return
}

// Implementation of appflinger.AppFlinger interface that just delegates to C Callbacks

func (self *AppflingerListener) Load(sessionId string, instanceId string, url string) (err error) {
	cSessionId := C.CString(sessionId)
	cInstanceId := C.CString(instanceId)
	cUrl := C.CString(url)
	rc := C.invoke_load(self.cb.load_cb, cSessionId, cInstanceId, cUrl)
	if rc != 0 {
		err = fmt.Errorf("Failed to load media")
	} else {
		err = nil
	}
	C.free(unsafe.Pointer(cSessionId))
	C.free(unsafe.Pointer(cInstanceId))
	C.free(unsafe.Pointer(cUrl))
	return
}

func (self *AppflingerListener) CancelLoad(sessionId string, instanceId string) (err error) {
	cSessionId := C.CString(sessionId)
	cInstanceId := C.CString(instanceId)
	rc := C.invoke_cancel_load(self.cb.cancel_load_cb, cSessionId, cInstanceId)
	if rc != 0 {
		err = fmt.Errorf("Failed to cancel load of media")
	} else {
		err = nil
	}
	C.free(unsafe.Pointer(cSessionId))
	C.free(unsafe.Pointer(cInstanceId))
	return
}

func (self *AppflingerListener) Pause(sessionId string, instanceId string) (err error) {
	cSessionId := C.CString(sessionId)
	cInstanceId := C.CString(instanceId)
	rc := C.invoke_pause(self.cb.pause_cb, cSessionId, cInstanceId)
	if rc != 0 {
		err = fmt.Errorf("Failed to pause media")
	} else {
		err = nil
	}
	C.free(unsafe.Pointer(cSessionId))
	C.free(unsafe.Pointer(cInstanceId))
	return
}

func (self *AppflingerListener) Play(sessionId string, instanceId string) (err error) {
	cSessionId := C.CString(sessionId)
	cInstanceId := C.CString(instanceId)
	rc := C.invoke_play(self.cb.play_cb, cSessionId, cInstanceId)
	if rc != 0 {
		err = fmt.Errorf("Failed to play media")
	} else {
		err = nil
	}
	C.free(unsafe.Pointer(cSessionId))
	C.free(unsafe.Pointer(cInstanceId))
	return
}

func (self *AppflingerListener) Seek(sessionId string, instanceId string, time float64) (err error) {
	cSessionId := C.CString(sessionId)
	cInstanceId := C.CString(instanceId)
	rc := C.invoke_seek(self.cb.seek_cb, cSessionId, cInstanceId, C.double(time))
	if rc != 0 {
		err = fmt.Errorf("Failed to seek media")
	} else {
		err = nil
	}
	C.free(unsafe.Pointer(cSessionId))
	C.free(unsafe.Pointer(cInstanceId))
	return
}

func (self *AppflingerListener) GetPaused(sessionId string, instanceId string) (paused bool, err error) {
	cSessionId := C.CString(sessionId)
	cInstanceId := C.CString(instanceId)
	var cPaused C.int
	rc := C.invoke_get_paused(self.cb.get_paused_cb, cSessionId, cInstanceId, &cPaused)
	if rc != 0 {
		err = fmt.Errorf("Failed to get pause state")
	} else {
		paused = GoBool(cPaused)
		err = nil
	}
	C.free(unsafe.Pointer(cSessionId))
	C.free(unsafe.Pointer(cInstanceId))
	return
}

func (self *AppflingerListener) GetSeeking(sessionId string, instanceId string) (seeking bool, err error) {
	cSessionId := C.CString(sessionId)
	cInstanceId := C.CString(instanceId)
	var cSeeking C.int
	rc := C.invoke_get_seeking(self.cb.get_seeking_cb, cSessionId, cInstanceId, &cSeeking)
	if rc != 0 {
		err = fmt.Errorf("Failed to get seeking state")
	} else {
		seeking = GoBool(cSeeking)
		err = nil
	}
	C.free(unsafe.Pointer(cSessionId))
	C.free(unsafe.Pointer(cInstanceId))
	return
}

func (self *AppflingerListener) GetDuration(sessionId string, instanceId string) (duration float64, err error) {
	cSessionId := C.CString(sessionId)
	cInstanceId := C.CString(instanceId)
	var cDuration C.double
	rc := C.invoke_get_duration(self.cb.get_duration_cb, cSessionId, cInstanceId, &cDuration)
	if rc != 0 {
		err = fmt.Errorf("Failed to get duration of media")
	} else {
		duration = float64(cDuration)
		err = nil
	}
	C.free(unsafe.Pointer(cSessionId))
	C.free(unsafe.Pointer(cInstanceId))
	return
}

func (self *AppflingerListener) GetCurrentTime(sessionId string, instanceId string) (time float64, err error) {
	cSessionId := C.CString(sessionId)
	cInstanceId := C.CString(instanceId)
	var cTime C.double
	rc := C.invoke_get_current_time(self.cb.get_current_time_cb, cSessionId, cInstanceId, &cTime)
	if rc != 0 {
		err = fmt.Errorf("Failed to get current time of media")
	} else {
		time = float64(cTime)
		err = nil
	}
	C.free(unsafe.Pointer(cSessionId))
	C.free(unsafe.Pointer(cInstanceId))
	return
}

func (self *AppflingerListener) GetNetworkState(sessionId string, instanceId string) (networkState int, err error) {
	cSessionId := C.CString(sessionId)
	cInstanceId := C.CString(instanceId)
	var cNetworkState C.int
	rc := C.invoke_get_network_state(self.cb.get_network_state_cb, cSessionId, cInstanceId, &cNetworkState)
	if rc != 0 {
		err = fmt.Errorf("Failed to get network state")
	} else {
		networkState = int(cNetworkState)
		err = nil
	}
	C.free(unsafe.Pointer(cSessionId))
	C.free(unsafe.Pointer(cInstanceId))
	return
}

func (self *AppflingerListener) GetReadyState(sessionId string, instanceId string) (readyState int, err error) {
	cSessionId := C.CString(sessionId)
	cInstanceId := C.CString(instanceId)
	var cReadyState C.int
	rc := C.invoke_get_ready_state(self.cb.get_ready_state_cb, cSessionId, cInstanceId, &cReadyState)
	if rc != 0 {
		err = fmt.Errorf("Failed to get ready state")
	} else {
		readyState = int(cReadyState)
		err = nil
	}
	C.free(unsafe.Pointer(cSessionId))
	C.free(unsafe.Pointer(cInstanceId))
	return
}

func (self *AppflingerListener) GetSeekable(sessionId string, instanceId string, result *appflinger.GetSeekableResult) (err error) {
	result.Start = []float64{0}
	result.End = []float64{MockDuration}
	err = nil
	return
}

func (self *AppflingerListener) GetBuffered(sessionId string, instanceId string, result *appflinger.GetBufferedResult) (err error) {
	result.Start = []float64{0}
	result.End = []float64{MockDuration}
	err = nil
	return
}

func (self *AppflingerListener) SetRect(sessionId string, instanceId string, x int, y int, width int, height int) (err error) {
	cSessionId := C.CString(sessionId)
	cInstanceId := C.CString(instanceId)
	rc := C.invoke_set_rect(self.cb.set_rect_cb, cSessionId, cInstanceId, C.int(x), C.int(y), C.int(width), C.int(height))
	if rc != 0 {
		err = fmt.Errorf("Failed to set media display rectangle")
	} else {
		err = nil
	}
	C.free(unsafe.Pointer(cSessionId))
	C.free(unsafe.Pointer(cInstanceId))
	return
}

func (self *AppflingerListener) SetVisible(sessionId string, instanceId string, visible bool) (err error) {
	err = nil
	return
}

func (self *AppflingerListener) SetRate(sessionId string, instanceId string, rate float64) (err error) {
	err = nil
	return
}

func (self *AppflingerListener) SetVolume(sessionId string, instanceId string, volume float64) (err error) {
	err = nil
	return
}

func (self *AppflingerListener) AddSourceBuffer(sessionId string, instanceId string, sourceId string, mimeType string) (err error) {
	err = nil
	return
}

func (self *AppflingerListener) RemoveSourceBuffer(sessionId string, instanceId string, sourceId string) (err error) {
	err = nil
	return
}

func (self *AppflingerListener) AbortSourceBuffer(sessionId string, instanceId string, sourceId string) (err error) {
	err = nil
	return
}

func (self *AppflingerListener) AppendBuffer(sessionId string, instanceId string, sourceId string, appendWindowStart float64, appendWindowEnd float64,
	bufferId string, bufferOffset int, bufferLength int, payload []byte, result *appflinger.GetBufferedResult) (err error) {
	result.Start = nil
	result.End = nil
	err = nil
	return
}

func (self *AppflingerListener) SetAppendMode(sessionId string, instanceId string, sourceId string, mode int) (err error) {
	err = nil
	return
}

func (self *AppflingerListener) SetAppendTimestampOffset(sessionId string, instanceId string, sourceId string, timestampOffset float64) (err error) {
	err = nil
	return
}

func (self *AppflingerListener) RemoveBufferRange(sessionId string, instanceId string, sourceId string, start float64, end float64) (err error) {
	err = nil
	return
}

func (self *AppflingerListener) ChangeSourceBufferType(sessionId string, instanceId string, sourceId string, mimeType string) (err error) {
	err = nil
	return
}

func (self *AppflingerListener) LoadResource(sessionId string, url string, method string, headers string, resourceId string,
	byteRangeStart int, byteRangeEnd int, sequenceNumber int, payload []byte, result *appflinger.LoadResourceResult) (err error) {
	err = nil
	result.Code = "404"
	result.Headers = ""
	result.BufferId = ""
	result.BufferLength = 0
	result.Payload = nil
	return
}

func (self *AppflingerListener) DeleteResource(sessionId string, BufferId string) (err error) {
	err = nil
	return
}

func (self *AppflingerListener) RequestKeySystem(sessionId string, keySystem string, supportedConfigurations []appflinger.EMEMediaKeySystemConfiguration, result *appflinger.RequestKeySystemResult) (err error) {
	err = nil
	return
}

func (self *AppflingerListener) CdmCreate(sessionId string, keySystem string, securityOrigin string, allowDistinctiveIdentifier bool, allowPersistentState bool) (cdmId string, err error) {
	err = nil
	return
}

func (self *AppflingerListener) CdmSetServerCertificate(sessionId string, cdmId string, payload []byte) (err error) {
	err = nil
	return
}

func (self *AppflingerListener) CdmSessionCreate(sessionId string, eventInstanceId string, cdmId string, sessionType string, initDataType string, payload []byte) (cdmSessionId string, expiration float64, err error) {
	err = nil
	return
}

func (self *AppflingerListener) CdmSessionUpdate(sessionId string, eventInstanceId string, cdmId string, cdmSessionId string, payload []byte) (err error) {
	err = nil
	return
}

func (self *AppflingerListener) CdmSessionLoad(sessionId string, eventInstanceId string, cdmId string, cdmSessionId string) (loaded bool, expiration float64, err error) {
	err = nil
	return
}

func (self *AppflingerListener) CdmSessionRemove(sessionId string, eventInstanceId string, cdmId string, cdmSessionId string) (err error) {
	err = nil
	return
}

func (self *AppflingerListener) CdmSessionClose(sessionId string, eventInstanceId string, cdmId string, cdmSessionId string) (err error) {
	err = nil
	return
}

func (self *AppflingerListener) SetCdm(sessionId string, instanceId string, cdmId string) (err error) {
	err = nil
	return
}

func (self *AppflingerListener) SendMessage(sessionId string, message string) (result string, err error) {
	err = nil
	result = ""
	return
}

func (self *AppflingerListener) OnPageLoad(sessionId string) (err error) {
	err = nil
	return
}

func (self *AppflingerListener) OnAddressBarChanged(sessionId string, url string) (err error) {
	err = nil
	return
}

func (self *AppflingerListener) OnTitleChanged(sessionId string, title string) (err error) {
	err = nil
	return
}

func (self *AppflingerListener) OnPageClose(sessionId string) (err error) {
	err = nil
	return
}

func (self *AppflingerListener) OnUIFrame(sessionId string, isCodecConfig bool, isKeyFrame bool, idx int, pts int, dts int, data []byte) (err error) {
	cSessionId := C.CString(sessionId)
	rc := C.invoke_on_ui_frame(self.cb.on_ui_frame_cb, cSessionId, CBool(isCodecConfig), CBool(isKeyFrame), C.int(idx), C.longlong(pts),
		C.longlong(dts), C.CBytes(data), C.uint(len(data)))
	if rc != 0 {
		err = fmt.Errorf("Failed to process frame")
	} else {
		err = nil
	}
	C.free(unsafe.Pointer(cSessionId))
	return
}
<commit_msg>C bindings - better handling of getBuffered() and getSeekable()<commit_after>// Copyright 2015 TVersity Inc. All rights reserved.
// Use of this source code is governed by an Apache 2.0
// license that can be found in the LICENSE file.

package main

import (
	// #include <stdio.h>
	// #include <stdlib.h>
	// #include "callbacks.h"
	"C"

	"github.com/tversity/appflinger-go"
)
import (
	"fmt"
	"unsafe"
)

const ()

// This struct will implement the appflinger.AppFlinger interface which is needed in order to
// receive the control channel commands and process them
type AppflingerListener struct {
	// C callback pointers
	// Note - we cannot invoke C function pointers from Go so we use a helper C function to do it
	// e.g. to invoke the on_ui_frame_cb function pointer we use C.invoke_on_ui_frame()
	cb *C.appflinger_callbacks_t
}

func NewAppflingerListener(cb *C.appflinger_callbacks_t) (self *AppflingerListener) {
	self = &AppflingerListener{}
	self.cb = cb
	return
}

// Implementation of appflinger.AppFlinger interface that just delegates to C Callbacks

func (self *AppflingerListener) Load(sessionId string, instanceId string, url string) (err error) {
	cSessionId := C.CString(sessionId)
	cInstanceId := C.CString(instanceId)
	cUrl := C.CString(url)
	rc := C.invoke_load(self.cb.load_cb, cSessionId, cInstanceId, cUrl)
	if rc != 0 {
		err = fmt.Errorf("Failed to load media")
	} else {
		err = nil
	}
	C.free(unsafe.Pointer(cSessionId))
	C.free(unsafe.Pointer(cInstanceId))
	C.free(unsafe.Pointer(cUrl))
	return
}

func (self *AppflingerListener) CancelLoad(sessionId string, instanceId string) (err error) {
	cSessionId := C.CString(sessionId)
	cInstanceId := C.CString(instanceId)
	rc := C.invoke_cancel_load(self.cb.cancel_load_cb, cSessionId, cInstanceId)
	if rc != 0 {
		err = fmt.Errorf("Failed to cancel load of media")
	} else {
		err = nil
	}
	C.free(unsafe.Pointer(cSessionId))
	C.free(unsafe.Pointer(cInstanceId))
	return
}

func (self *AppflingerListener) Pause(sessionId string, instanceId string) (err error) {
	cSessionId := C.CString(sessionId)
	cInstanceId := C.CString(instanceId)
	rc := C.invoke_pause(self.cb.pause_cb, cSessionId, cInstanceId)
	if rc != 0 {
		err = fmt.Errorf("Failed to pause media")
	} else {
		err = nil
	}
	C.free(unsafe.Pointer(cSessionId))
	C.free(unsafe.Pointer(cInstanceId))
	return
}

func (self *AppflingerListener) Play(sessionId string, instanceId string) (err error) {
	cSessionId := C.CString(sessionId)
	cInstanceId := C.CString(instanceId)
	rc := C.invoke_play(self.cb.play_cb, cSessionId, cInstanceId)
	if rc != 0 {
		err = fmt.Errorf("Failed to play media")
	} else {
		err = nil
	}
	C.free(unsafe.Pointer(cSessionId))
	C.free(unsafe.Pointer(cInstanceId))
	return
}

func (self *AppflingerListener) Seek(sessionId string, instanceId string, time float64) (err error) {
	cSessionId := C.CString(sessionId)
	cInstanceId := C.CString(instanceId)
	rc := C.invoke_seek(self.cb.seek_cb, cSessionId, cInstanceId, C.double(time))
	if rc != 0 {
		err = fmt.Errorf("Failed to seek media")
	} else {
		err = nil
	}
	C.free(unsafe.Pointer(cSessionId))
	C.free(unsafe.Pointer(cInstanceId))
	return
}

func (self *AppflingerListener) GetPaused(sessionId string, instanceId string) (paused bool, err error) {
	cSessionId := C.CString(sessionId)
	cInstanceId := C.CString(instanceId)
	var cPaused C.int
	rc := C.invoke_get_paused(self.cb.get_paused_cb, cSessionId, cInstanceId, &cPaused)
	if rc != 0 {
		err = fmt.Errorf("Failed to get pause state")
	} else {
		paused = GoBool(cPaused)
		err = nil
	}
	C.free(unsafe.Pointer(cSessionId))
	C.free(unsafe.Pointer(cInstanceId))
	return
}

func (self *AppflingerListener) GetSeeking(sessionId string, instanceId string) (seeking bool, err error) {
	cSessionId := C.CString(sessionId)
	cInstanceId := C.CString(instanceId)
	var cSeeking C.int
	rc := C.invoke_get_seeking(self.cb.get_seeking_cb, cSessionId, cInstanceId, &cSeeking)
	if rc != 0 {
		err = fmt.Errorf("Failed to get seeking state")
	} else {
		seeking = GoBool(cSeeking)
		err = nil
	}
	C.free(unsafe.Pointer(cSessionId))
	C.free(unsafe.Pointer(cInstanceId))
	return
}

func (self *AppflingerListener) GetDuration(sessionId string, instanceId string) (duration float64, err error) {
	cSessionId := C.CString(sessionId)
	cInstanceId := C.CString(instanceId)
	var cDuration C.double
	rc := C.invoke_get_duration(self.cb.get_duration_cb, cSessionId, cInstanceId, &cDuration)
	if rc != 0 {
		err = fmt.Errorf("Failed to get duration of media")
	} else {
		duration = float64(cDuration)
		err = nil
	}
	C.free(unsafe.Pointer(cSessionId))
	C.free(unsafe.Pointer(cInstanceId))
	return
}

func (self *AppflingerListener) GetCurrentTime(sessionId string, instanceId string) (time float64, err error) {
	cSessionId := C.CString(sessionId)
	cInstanceId := C.CString(instanceId)
	var cTime C.double
	rc := C.invoke_get_current_time(self.cb.get_current_time_cb, cSessionId, cInstanceId, &cTime)
	if rc != 0 {
		err = fmt.Errorf("Failed to get current time of media")
	} else {
		time = float64(cTime)
		err = nil
	}
	C.free(unsafe.Pointer(cSessionId))
	C.free(unsafe.Pointer(cInstanceId))
	return
}

func (self *AppflingerListener) GetNetworkState(sessionId string, instanceId string) (networkState int, err error) {
	cSessionId := C.CString(sessionId)
	cInstanceId := C.CString(instanceId)
	var cNetworkState C.int
	rc := C.invoke_get_network_state(self.cb.get_network_state_cb, cSessionId, cInstanceId, &cNetworkState)
	if rc != 0 {
		err = fmt.Errorf("Failed to get network state")
	} else {
		networkState = int(cNetworkState)
		err = nil
	}
	C.free(unsafe.Pointer(cSessionId))
	C.free(unsafe.Pointer(cInstanceId))
	return
}

func (self *AppflingerListener) GetReadyState(sessionId string, instanceId string) (readyState int, err error) {
	cSessionId := C.CString(sessionId)
	cInstanceId := C.CString(instanceId)
	var cReadyState C.int
	rc := C.invoke_get_ready_state(self.cb.get_ready_state_cb, cSessionId, cInstanceId, &cReadyState)
	if rc != 0 {
		err = fmt.Errorf("Failed to get ready state")
	} else {
		readyState = int(cReadyState)
		err = nil
	}
	C.free(unsafe.Pointer(cSessionId))
	C.free(unsafe.Pointer(cInstanceId))
	return
}

func (self *AppflingerListener) GetSeekable(sessionId string, instanceId string, result *appflinger.GetSeekableResult) (err error) {
	var duration float64
	duration, err = self.GetDuration(sessionId, instanceId)
	if err != nil {
		return
	}

	result.Start = []float64{0}
	result.End = []float64{duration}
	err = nil
	return
}

func (self *AppflingerListener) GetBuffered(sessionId string, instanceId string, result *appflinger.GetBufferedResult) (err error) {
	var duration float64
	duration, err = self.GetDuration(sessionId, instanceId)
	if err != nil {
		return
	}

	result.Start = []float64{0}
	result.End = []float64{duration}
	err = nil
	return
}

func (self *AppflingerListener) SetRect(sessionId string, instanceId string, x int, y int, width int, height int) (err error) {
	cSessionId := C.CString(sessionId)
	cInstanceId := C.CString(instanceId)
	rc := C.invoke_set_rect(self.cb.set_rect_cb, cSessionId, cInstanceId, C.int(x), C.int(y), C.int(width), C.int(height))
	if rc != 0 {
		err = fmt.Errorf("Failed to set media display rectangle")
	} else {
		err = nil
	}
	C.free(unsafe.Pointer(cSessionId))
	C.free(unsafe.Pointer(cInstanceId))
	return
}

func (self *AppflingerListener) SetVisible(sessionId string, instanceId string, visible bool) (err error) {
	err = nil
	return
}

func (self *AppflingerListener) SetRate(sessionId string, instanceId string, rate float64) (err error) {
	err = nil
	return
}

func (self *AppflingerListener) SetVolume(sessionId string, instanceId string, volume float64) (err error) {
	err = nil
	return
}

func (self *AppflingerListener) AddSourceBuffer(sessionId string, instanceId string, sourceId string, mimeType string) (err error) {
	err = nil
	return
}

func (self *AppflingerListener) RemoveSourceBuffer(sessionId string, instanceId string, sourceId string) (err error) {
	err = nil
	return
}

func (self *AppflingerListener) AbortSourceBuffer(sessionId string, instanceId string, sourceId string) (err error) {
	err = nil
	return
}

func (self *AppflingerListener) AppendBuffer(sessionId string, instanceId string, sourceId string, appendWindowStart float64, appendWindowEnd float64,
	bufferId string, bufferOffset int, bufferLength int, payload []byte, result *appflinger.GetBufferedResult) (err error) {
	result.Start = nil
	result.End = nil
	err = nil
	return
}

func (self *AppflingerListener) SetAppendMode(sessionId string, instanceId string, sourceId string, mode int) (err error) {
	err = nil
	return
}

func (self *AppflingerListener) SetAppendTimestampOffset(sessionId string, instanceId string, sourceId string, timestampOffset float64) (err error) {
	err = nil
	return
}

func (self *AppflingerListener) RemoveBufferRange(sessionId string, instanceId string, sourceId string, start float64, end float64) (err error) {
	err = nil
	return
}

func (self *AppflingerListener) ChangeSourceBufferType(sessionId string, instanceId string, sourceId string, mimeType string) (err error) {
	err = nil
	return
}

func (self *AppflingerListener) LoadResource(sessionId string, url string, method string, headers string, resourceId string,
	byteRangeStart int, byteRangeEnd int, sequenceNumber int, payload []byte, result *appflinger.LoadResourceResult) (err error) {
	err = nil
	result.Code = "404"
	result.Headers = ""
	result.BufferId = ""
	result.BufferLength = 0
	result.Payload = nil
	return
}

func (self *AppflingerListener) DeleteResource(sessionId string, BufferId string) (err error) {
	err = nil
	return
}

func (self *AppflingerListener) RequestKeySystem(sessionId string, keySystem string, supportedConfigurations []appflinger.EMEMediaKeySystemConfiguration, result *appflinger.RequestKeySystemResult) (err error) {
	err = nil
	return
}

func (self *AppflingerListener) CdmCreate(sessionId string, keySystem string, securityOrigin string, allowDistinctiveIdentifier bool, allowPersistentState bool) (cdmId string, err error) {
	err = nil
	return
}

func (self *AppflingerListener) CdmSetServerCertificate(sessionId string, cdmId string, payload []byte) (err error) {
	err = nil
	return
}

func (self *AppflingerListener) CdmSessionCreate(sessionId string, eventInstanceId string, cdmId string, sessionType string, initDataType string, payload []byte) (cdmSessionId string, expiration float64, err error) {
	err = nil
	return
}

func (self *AppflingerListener) CdmSessionUpdate(sessionId string, eventInstanceId string, cdmId string, cdmSessionId string, payload []byte) (err error) {
	err = nil
	return
}

func (self *AppflingerListener) CdmSessionLoad(sessionId string, eventInstanceId string, cdmId string, cdmSessionId string) (loaded bool, expiration float64, err error) {
	err = nil
	return
}

func (self *AppflingerListener) CdmSessionRemove(sessionId string, eventInstanceId string, cdmId string, cdmSessionId string) (err error) {
	err = nil
	return
}

func (self *AppflingerListener) CdmSessionClose(sessionId string, eventInstanceId string, cdmId string, cdmSessionId string) (err error) {
	err = nil
	return
}

func (self *AppflingerListener) SetCdm(sessionId string, instanceId string, cdmId string) (err error) {
	err = nil
	return
}

func (self *AppflingerListener) SendMessage(sessionId string, message string) (result string, err error) {
	err = nil
	result = ""
	return
}

func (self *AppflingerListener) OnPageLoad(sessionId string) (err error) {
	err = nil
	return
}

func (self *AppflingerListener) OnAddressBarChanged(sessionId string, url string) (err error) {
	err = nil
	return
}

func (self *AppflingerListener) OnTitleChanged(sessionId string, title string) (err error) {
	err = nil
	return
}

func (self *AppflingerListener) OnPageClose(sessionId string) (err error) {
	err = nil
	return
}

func (self *AppflingerListener) OnUIFrame(sessionId string, isCodecConfig bool, isKeyFrame bool, idx int, pts int, dts int, data []byte) (err error) {
	cSessionId := C.CString(sessionId)
	rc := C.invoke_on_ui_frame(self.cb.on_ui_frame_cb, cSessionId, CBool(isCodecConfig), CBool(isKeyFrame), C.int(idx), C.longlong(pts),
		C.longlong(dts), C.CBytes(data), C.uint(len(data)))
	if rc != 0 {
		err = fmt.Errorf("Failed to process frame")
	} else {
		err = nil
	}
	C.free(unsafe.Pointer(cSessionId))
	return
}
<|endoftext|>


<commit_before><commit_msg>mssql: use column name in index name instead of 'osm_id'<commit_after><|endoftext|>


<commit_before>package device

import (
	"bytes"
	"fmt"
	"os"
	"sync"
	"time"

	"github.com/Comcast/webpa-common/logging"
	"github.com/Comcast/webpa-common/wrp"
)

func expectMessage(c Connection) (*wrp.Message, error) {
	var frame bytes.Buffer
	if ok, err := c.Read(&frame); !ok || err != nil {
		return nil, fmt.Errorf("Read failed: %s", err)
	}

	var (
		message = new(wrp.Message)
		decoder = wrp.NewDecoder(&frame, wrp.Msgpack)
	)

	if err := decoder.Decode(message); err != nil {
		return nil, fmt.Errorf("Could not decode message: %s", err)
	}

	return message, nil
}

func writeMessage(m *wrp.Message, c Connection) error {
	if frame, err := c.NextWriter(); err != nil {
		return err
	} else {
		encoder := wrp.NewEncoder(frame, wrp.Msgpack)
		if err := encoder.Encode(m); err != nil {
			return err
		}

		return frame.Close()
	}
}

func ExampleManagerTransaction() {
	var (
		authStatusSent = new(sync.WaitGroup)

		options = &Options{
			Logger:    logging.DefaultLogger(),
			AuthDelay: 250 * time.Millisecond,
			Listeners: []Listener{
				func(e *Event) {
					switch e.Type {
					case Connect:
						fmt.Printf("%s connected\n", e.Device.ID())
						authStatusSent.Add(1)
					case MessageSent:
						if e.Message.MessageType() == wrp.AuthMessageType {
							fmt.Println("auth status sent")
							authStatusSent.Done()
						} else {
							fmt.Println("message sent")
						}
					case TransactionComplete:
						fmt.Println("response received")
					}
				},
			},
		}

		manager, server, websocketURL = startWebsocketServer(options)

		dialer                   = NewDialer(options, nil)
		connection, _, dialError = dialer.Dial(
			websocketURL,
			"mac:111122223333",
			nil,
			nil,
		)
	)

	defer server.Close()
	if dialError != nil {
		fmt.Fprintf(os.Stderr, "Dial error: %s\n", dialError)
		return
	}

	defer connection.Close()

	// this is our "device" goroutine
	go func() {
		if message, err := expectMessage(connection); err != nil {
			fmt.Fprintf(os.Stderr, err.Error())
			return
		} else if message.Type != wrp.AuthMessageType {
			fmt.Fprintf(os.Stderr, "Expected auth status, but got: %s", message.Type)
			return
		} else if message.Status == nil {
			fmt.Fprintf(os.Stderr, "No auth status code")
			return
		} else if *message.Status != wrp.AuthStatusAuthorized {
			fmt.Fprintf(os.Stderr, "Expected authorized, but got: %d", *message.Status)
			return
		} else {
			fmt.Println("auth status received")
		}

		if message, err := expectMessage(connection); err != nil {
			fmt.Fprintf(os.Stderr, err.Error())
			return
		} else if message.Type != wrp.SimpleRequestResponseMessageType {
			fmt.Fprintf(os.Stderr, "Expected request/response, but got: %s", message.Type)
			return
		} else {
			fmt.Println("message received")
			deviceResponse := *message
			deviceResponse.Source = message.Destination
			deviceResponse.Destination = message.Source
			deviceResponse.Payload = []byte("Homer Simpson, Smiling Politely")

			if err := writeMessage(&deviceResponse, connection); err != nil {
				fmt.Fprintf(os.Stderr, "Unable to write response: %s", err)
				return
			}
		}
	}()

	// Before sending our request, wait for the server to send the auth status
	authStatusSent.Wait()

	// Route will block until the corresponding message returns from the device
	response, err := manager.Route(
		&Request{
			Message: &wrp.SimpleRequestResponse{
				Source:          "Example",
				Destination:     "mac:111122223333",
				Payload:         []byte("Billy Corgan, Smashing Pumpkins"),
				TransactionUUID: "MyTransactionUUID",
			},
		},
	)

	if err != nil {
		fmt.Fprintf(os.Stderr, "Route error: %s\n", err)
	} else if response != nil {
		fmt.Printf("(%s): %s to %s -> %s\n", response.Message.TransactionUUID, response.Message.Source, response.Message.Destination, response.Message.Payload)
	}

	// Output:
	// mac:111122223333 connected
	// auth status sent
	// auth status received
	// message sent
	// message received
	// response received
	// (MyTransactionUUID): mac:111122223333 to Example -> Homer Simpson, Smiling Politely
}
<commit_msg>Tweaked the synchronization to avoid a race condition with the golang example code wrapper<commit_after>package device

import (
	"bytes"
	"fmt"
	"os"
	"sync"
	"time"

	"github.com/Comcast/webpa-common/logging"
	"github.com/Comcast/webpa-common/wrp"
)

func expectMessage(c Connection) (*wrp.Message, error) {
	var frame bytes.Buffer
	if ok, err := c.Read(&frame); !ok || err != nil {
		return nil, fmt.Errorf("Read failed: %s", err)
	}

	var (
		message = new(wrp.Message)
		decoder = wrp.NewDecoder(&frame, wrp.Msgpack)
	)

	if err := decoder.Decode(message); err != nil {
		return nil, fmt.Errorf("Could not decode message: %s", err)
	}

	return message, nil
}

func writeMessage(m *wrp.Message, c Connection) error {
	if frame, err := c.NextWriter(); err != nil {
		return err
	} else {
		encoder := wrp.NewEncoder(frame, wrp.Msgpack)
		if err := encoder.Encode(m); err != nil {
			return err
		}

		return frame.Close()
	}
}

func ExampleManagerTransaction() {
	disconnected := new(sync.WaitGroup)
	disconnected.Add(1)

	var (
		options = &Options{
			Logger:    logging.DefaultLogger(),
			AuthDelay: 250 * time.Millisecond,
			Listeners: []Listener{
				func(e *Event) {
					switch e.Type {
					case Connect:
						fmt.Printf("%s connected\n", e.Device.ID())
					case MessageSent:
						if e.Message.MessageType() == wrp.AuthMessageType {
							fmt.Println("auth status sent")
						} else {
							fmt.Println("message sent")
						}
					case TransactionComplete:
						fmt.Println("response received")
					case Disconnect:
						fmt.Printf("%s disconnected\n", e.Device.ID())
						disconnected.Done()
					}
				},
			},
		}

		manager, server, websocketURL = startWebsocketServer(options)

		dialer                   = NewDialer(options, nil)
		connection, _, dialError = dialer.Dial(
			websocketURL,
			"mac:111122223333",
			nil,
			nil,
		)
	)

	defer server.Close()
	if dialError != nil {
		fmt.Fprintf(os.Stderr, "Dial error: %s\n", dialError)
		return
	}

	// grab the auth status message that should be coming
	if message, err := expectMessage(connection); err != nil {
		fmt.Fprintf(os.Stderr, err.Error())
		return
	} else if message.Type != wrp.AuthMessageType {
		fmt.Fprintf(os.Stderr, "Expected auth status, but got: %s", message.Type)
		return
	} else if message.Status == nil {
		fmt.Fprintf(os.Stderr, "No auth status code")
		return
	} else if *message.Status != wrp.AuthStatusAuthorized {
		fmt.Fprintf(os.Stderr, "Expected authorized, but got: %d", *message.Status)
		return
	} else {
		fmt.Println("auth status received")
	}

	// spawn a go routine to respond to our routed message
	go func() {
		if message, err := expectMessage(connection); err != nil {
			fmt.Fprintf(os.Stderr, err.Error())
			return
		} else if message.Type != wrp.SimpleRequestResponseMessageType {
			fmt.Fprintf(os.Stderr, "Expected request/response, but got: %s", message.Type)
			return
		} else {
			fmt.Println("message received")
			deviceResponse := *message
			deviceResponse.Source = message.Destination
			deviceResponse.Destination = message.Source
			deviceResponse.Payload = []byte("Homer Simpson, Smiling Politely")

			if err := writeMessage(&deviceResponse, connection); err != nil {
				fmt.Fprintf(os.Stderr, "Unable to write response: %s", err)
				return
			}
		}
	}()

	// Route will block until the corresponding message returns from the device
	response, err := manager.Route(
		&Request{
			Message: &wrp.SimpleRequestResponse{
				Source:          "Example",
				Destination:     "mac:111122223333",
				Payload:         []byte("Billy Corgan, Smashing Pumpkins"),
				TransactionUUID: "MyTransactionUUID",
			},
		},
	)

	if err != nil {
		fmt.Fprintf(os.Stderr, "Route error: %s\n", err)
	} else if response != nil {
		fmt.Printf("(%s): %s to %s -> %s\n", response.Message.TransactionUUID, response.Message.Source, response.Message.Destination, response.Message.Payload)
	}

	// close the connection and ensure that the Disconnect event gets sent
	// before continuing.  This prevents a race condition with the example code
	// wrapper that swaps out the stream written to by the fmt.Print* functions.
	connection.Close()
	disconnected.Wait()

	// Output:
	// mac:111122223333 connected
	// auth status sent
	// auth status received
	// message sent
	// message received
	// response received
	// (MyTransactionUUID): mac:111122223333 to Example -> Homer Simpson, Smiling Politely
	// mac:111122223333 disconnected
}
<|endoftext|>


<commit_before>// Copyright 2015 CoreOS, 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 main

import (
	"crypto/rand"
	"fmt"
	"log"
	"os"
	"time"

	mrand "math/rand"

	clientv2 "github.com/coreos/etcd/client"
	"github.com/coreos/etcd/clientv3"
	consulapi "github.com/hashicorp/consul/api"
	"github.com/samuel/go-zookeeper/zk"
)

var (
	// dialTotal counts the number of mustCreateConn calls so that endpoint
	// connections can be handed out in round-robin order
	dialTotal int
)

func mustCreateConn() *clientv3.Client {
	endpoint := endpoints[dialTotal%len(endpoints)]
	dialTotal++
	cfg := clientv3.Config{Endpoints: []string{endpoint}}
	client, err := clientv3.New(cfg)
	if err != nil {
		fmt.Fprintf(os.Stderr, "dial error: %v\n", err)
		os.Exit(1)
	}
	return client
}

func mustCreateClients(totalClients, totalConns uint) []*clientv3.Client {
	conns := make([]*clientv3.Client, totalConns)
	for i := range conns {
		conns[i] = mustCreateConn()
	}

	clients := make([]*clientv3.Client, totalClients)
	for i := range clients {
		clients[i] = conns[i%int(totalConns)]
	}
	return clients
}

func mustCreateClientsEtcd2(total uint) []clientv2.KeysAPI {
	cks := make([]clientv2.KeysAPI, total)
	for i := range cks {
		endpoint := endpoints[dialTotal%len(endpoints)]
		dialTotal++

		cfg := clientv2.Config{
			Endpoints:               []string{endpoint},
			Transport:               clientv2.DefaultTransport,
			HeaderTimeoutPerRequest: time.Second,
		}
		c, err := clientv2.New(cfg)
		if err != nil {
			log.Fatal(err)
		}
		kapi := clientv2.NewKeysAPI(c)

		cks[i] = kapi
	}
	return cks
}

func mustCreateConnsZk(total uint) []*zk.Conn {
	zks := make([]*zk.Conn, total)
	for i := range zks {
		endpoint := endpoints[dialTotal%len(endpoints)]
		dialTotal++
		conn, _, err := zk.Connect([]string{endpoint}, time.Second)
		if err != nil {
			log.Fatal(err)
		}
		zks[i] = conn
	}
	return zks
}

func mustCreateConnsConsul(total uint) []*consulapi.KV {
	css := make([]*consulapi.KV, total)
	for i := range css {
		endpoint := endpoints[dialTotal%len(endpoints)]
		dialTotal++

		dcfg := consulapi.DefaultConfig()
		dcfg.Address = endpoint // x.x.x.x:8500
		cli, err := consulapi.NewClient(dcfg)
		if err != nil {
			log.Fatal(err)
		}

		css[i] = cli.KV()
	}
	return css
}

func mustRandBytes(n int) []byte {
	rb := make([]byte, n)
	_, err := rand.Read(rb)
	if err != nil {
		fmt.Fprintf(os.Stderr, "failed to generate value: %v\n", err)
		os.Exit(1)
	}
	return rb
}

func randBytes(bytesN int) []byte {
	const (
		letterBytes   = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
		letterIdxBits = 6                    // 6 bits to represent a letter index
		letterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits
		letterIdxMax  = 63 / letterIdxBits   // # of letter indices fitting in 63 bits
	)
	src := mrand.NewSource(time.Now().UnixNano())
	b := make([]byte, bytesN)
	for i, cache, remain := bytesN-1, src.Int63(), letterIdxMax; i >= 0; {
		if remain == 0 {
			cache, remain = src.Int63(), letterIdxMax
		}
		if idx := int(cache & letterIdxMask); idx < len(letterBytes) {
			b[i] = letterBytes[idx]
			i--
		}
		cache >>= letterIdxBits
		remain--
	}
	return b
}

func multiRandBytes(bytesN, sliceN int) [][]byte {
	m := make(map[string]struct{})
	var rs [][]byte
	for len(rs) != sliceN {
		b := randBytes(bytesN)
		if _, ok := m[string(b)]; !ok {
			rs = append(rs, b)
			m[string(b)] = struct{}{}
		}
	}
	return rs
}

func toFile(txt, fpath string) error {
	f, err := os.OpenFile(fpath, os.O_RDWR|os.O_TRUNC, 0777)
	if err != nil {
		f, err = os.Create(fpath)
		if err != nil {
			return err
		}
	}
	defer f.Close()
	if _, err := f.WriteString(txt); err != nil {
		return err
	}
	return nil
}

func toMillisecond(d time.Duration) float64 {
	return d.Seconds() * 1000
}
<commit_msg>bench: fix endpoint schema for etcd2<commit_after>// Copyright 2015 CoreOS, 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 main

import (
	"crypto/rand"
	"fmt"
	"log"
	"os"
	"strings"
	"time"

	mrand "math/rand"

	clientv2 "github.com/coreos/etcd/client"
	"github.com/coreos/etcd/clientv3"
	consulapi "github.com/hashicorp/consul/api"
	"github.com/samuel/go-zookeeper/zk"
)

var (
	// dialTotal counts the number of mustCreateConn calls so that endpoint
	// connections can be handed out in round-robin order
	dialTotal int
)

func mustCreateConn() *clientv3.Client {
	endpoint := endpoints[dialTotal%len(endpoints)]
	dialTotal++
	cfg := clientv3.Config{Endpoints: []string{endpoint}}
	client, err := clientv3.New(cfg)
	if err != nil {
		fmt.Fprintf(os.Stderr, "dial error: %v\n", err)
		os.Exit(1)
	}
	return client
}

func mustCreateClients(totalClients, totalConns uint) []*clientv3.Client {
	conns := make([]*clientv3.Client, totalConns)
	for i := range conns {
		conns[i] = mustCreateConn()
	}

	clients := make([]*clientv3.Client, totalClients)
	for i := range clients {
		clients[i] = conns[i%int(totalConns)]
	}
	return clients
}

func mustCreateClientsEtcd2(total uint) []clientv2.KeysAPI {
	cks := make([]clientv2.KeysAPI, total)
	for i := range cks {
		endpoint := endpoints[dialTotal%len(endpoints)]
		dialTotal++

		if !strings.HasPrefix(endpoint, "http://") {
			endpoint = "http://" + endpoint
		}
		cfg := clientv2.Config{
			Endpoints:               []string{endpoint},
			Transport:               clientv2.DefaultTransport,
			HeaderTimeoutPerRequest: time.Second,
		}
		c, err := clientv2.New(cfg)
		if err != nil {
			log.Fatal(err)
		}
		kapi := clientv2.NewKeysAPI(c)

		cks[i] = kapi
	}
	return cks
}

func mustCreateConnsZk(total uint) []*zk.Conn {
	zks := make([]*zk.Conn, total)
	for i := range zks {
		endpoint := endpoints[dialTotal%len(endpoints)]
		dialTotal++
		conn, _, err := zk.Connect([]string{endpoint}, time.Second)
		if err != nil {
			log.Fatal(err)
		}
		zks[i] = conn
	}
	return zks
}

func mustCreateConnsConsul(total uint) []*consulapi.KV {
	css := make([]*consulapi.KV, total)
	for i := range css {
		endpoint := endpoints[dialTotal%len(endpoints)]
		dialTotal++

		dcfg := consulapi.DefaultConfig()
		dcfg.Address = endpoint // x.x.x.x:8500
		cli, err := consulapi.NewClient(dcfg)
		if err != nil {
			log.Fatal(err)
		}

		css[i] = cli.KV()
	}
	return css
}

func mustRandBytes(n int) []byte {
	rb := make([]byte, n)
	_, err := rand.Read(rb)
	if err != nil {
		fmt.Fprintf(os.Stderr, "failed to generate value: %v\n", err)
		os.Exit(1)
	}
	return rb
}

func randBytes(bytesN int) []byte {
	const (
		letterBytes   = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
		letterIdxBits = 6                    // 6 bits to represent a letter index
		letterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits
		letterIdxMax  = 63 / letterIdxBits   // # of letter indices fitting in 63 bits
	)
	src := mrand.NewSource(time.Now().UnixNano())
	b := make([]byte, bytesN)
	for i, cache, remain := bytesN-1, src.Int63(), letterIdxMax; i >= 0; {
		if remain == 0 {
			cache, remain = src.Int63(), letterIdxMax
		}
		if idx := int(cache & letterIdxMask); idx < len(letterBytes) {
			b[i] = letterBytes[idx]
			i--
		}
		cache >>= letterIdxBits
		remain--
	}
	return b
}

func multiRandBytes(bytesN, sliceN int) [][]byte {
	m := make(map[string]struct{})
	var rs [][]byte
	for len(rs) != sliceN {
		b := randBytes(bytesN)
		if _, ok := m[string(b)]; !ok {
			rs = append(rs, b)
			m[string(b)] = struct{}{}
		}
	}
	return rs
}

func toFile(txt, fpath string) error {
	f, err := os.OpenFile(fpath, os.O_RDWR|os.O_TRUNC, 0777)
	if err != nil {
		f, err = os.Create(fpath)
		if err != nil {
			return err
		}
	}
	defer f.Close()
	if _, err := f.WriteString(txt); err != nil {
		return err
	}
	return nil
}

func toMillisecond(d time.Duration) float64 {
	return d.Seconds() * 1000
}
<|endoftext|>


<commit_before>package mdb

import (
	crand "crypto/rand"
	"io/ioutil"
	"math/rand"
	"os"
	"testing"
)

// repeatedly put (overwrite) keys.
func BenchmarkTxnPut(b *testing.B) {
	initRandSource(b)
	env, path := setupBenchDB(b)
	defer teardownBenchDB(b, env, path)

	dbi := openBenchDBI(b, env)

	var ps [][]byte

	rc := newRandSourceCursor()
	txn, err := env.BeginTxn(nil, 0)
	bMust(b, err, "starting transaction")
	for i := 0; i < benchDBNumKeys; i++ {
		k := makeBenchDBKey(&rc)
		v := makeBenchDBVal(&rc)
		err := txn.Put(dbi, k, v, 0)
		ps = append(ps, k, v)
		bTxnMust(b, txn, err, "putting data")
	}
	err = txn.Commit()
	bMust(b, err, "commiting transaction")

	txn, err = env.BeginTxn(nil, 0)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		k := ps[rand.Intn(len(ps)/2)*2]
		v := makeBenchDBVal(&rc)
		err := txn.Put(dbi, k, v, 0)
		bTxnMust(b, txn, err, "putting data")
	}
	b.StopTimer()
	err = txn.Commit()
	bMust(b, err, "commiting transaction")
}

// repeatedly get random keys.
func BenchmarkTxnGetRDONLY(b *testing.B) {
	initRandSource(b)
	env, path := setupBenchDB(b)
	defer teardownBenchDB(b, env, path)

	dbi := openBenchDBI(b, env)

	var ps [][]byte

	rc := newRandSourceCursor()
	txn, err := env.BeginTxn(nil, 0)
	bMust(b, err, "starting transaction")
	for i := 0; i < benchDBNumKeys; i++ {
		k := makeBenchDBKey(&rc)
		v := makeBenchDBVal(&rc)
		err := txn.Put(dbi, k, v, 0)
		ps = append(ps, k, v)
		bTxnMust(b, txn, err, "putting data")
	}
	err = txn.Commit()
	bMust(b, err, "commiting transaction")

	txn, err = env.BeginTxn(nil, RDONLY)
	bMust(b, err, "starting transaction")
	defer txn.Abort()
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_, err := txn.Get(dbi, ps[rand.Intn(len(ps))])
		if err == NotFound {
			continue
		}
		if err != nil {
			b.Fatalf("error getting data: %v", err)
		}
	}
	b.StopTimer()
}

// like BenchmarkTxnGetRDONLY, but txn.GetVal() is called instead.
func BenchmarkTxnGetValRDONLY(b *testing.B) {
	initRandSource(b)
	env, path := setupBenchDB(b)
	defer teardownBenchDB(b, env, path)

	dbi := openBenchDBI(b, env)

	var ps [][]byte

	rc := newRandSourceCursor()
	txn, err := env.BeginTxn(nil, 0)
	bMust(b, err, "starting transaction")
	for i := 0; i < benchDBNumKeys; i++ {
		k := makeBenchDBKey(&rc)
		v := makeBenchDBVal(&rc)
		err := txn.Put(dbi, k, v, 0)
		ps = append(ps, k, v)
		bTxnMust(b, txn, err, "putting data")
	}
	err = txn.Commit()
	bMust(b, err, "commiting transaction")

	txn, err = env.BeginTxn(nil, RDONLY)
	bMust(b, err, "starting transaction")
	defer txn.Abort()
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_, err := txn.GetVal(dbi, ps[rand.Intn(len(ps))])
		if err == NotFound {
			continue
		}
		if err != nil {
			b.Fatalf("error getting data: %v", err)
		}
	}
	b.StopTimer()
}

// repeatedly scan all the values in a database.
func BenchmarkCursorScanRDONLY(b *testing.B) {
	initRandSource(b)
	env, path := setupBenchDB(b)
	defer teardownBenchDB(b, env, path)

	dbi := openBenchDBI(b, env)

	var ps [][]byte

	rc := newRandSourceCursor()
	txn, err := env.BeginTxn(nil, 0)
	bMust(b, err, "starting transaction")
	for i := 0; i < benchDBNumKeys; i++ {
		k := makeBenchDBKey(&rc)
		v := makeBenchDBVal(&rc)
		err := txn.Put(dbi, k, v, 0)
		ps = append(ps, k, v)
		bTxnMust(b, txn, err, "putting data")
	}
	err = txn.Commit()
	bMust(b, err, "commiting transaction")

	txn, err = env.BeginTxn(nil, RDONLY)
	bMust(b, err, "starting transaction")
	defer txn.Abort()
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		func() {
			cur, err := txn.CursorOpen(dbi)
			bMust(b, err, "opening cursor")
			defer cur.Close()
			var count int64
			for {
				_, _, err := cur.Get(nil, nil, NEXT)
				if err == NotFound {
					return
				}
				if err != nil {
					b.Fatalf("error getting data: %v", err)
				}
				count++
			}
			if count != benchDBNumKeys {
				b.Fatalf("unexpected number of keys: %d", count)
			}
		}()
	}
	b.StopTimer()
}

// like BenchmarkCursoreScanRDONLY, but cursor.GetVal() is called instead.
func BenchmarkCursorScanValRDONLY(b *testing.B) {
	initRandSource(b)
	env, path := setupBenchDB(b)
	defer teardownBenchDB(b, env, path)

	dbi := openBenchDBI(b, env)

	var ps [][]byte

	rc := newRandSourceCursor()
	txn, err := env.BeginTxn(nil, 0)
	bMust(b, err, "starting transaction")
	for i := 0; i < benchDBNumKeys; i++ {
		k := makeBenchDBKey(&rc)
		v := makeBenchDBVal(&rc)
		err := txn.Put(dbi, k, v, 0)
		ps = append(ps, k, v)
		bTxnMust(b, txn, err, "putting data")
	}
	err = txn.Commit()
	bMust(b, err, "commiting transaction")

	txn, err = env.BeginTxn(nil, RDONLY)
	bMust(b, err, "starting transaction")
	defer txn.Abort()
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		func() {
			cur, err := txn.CursorOpen(dbi)
			bMust(b, err, "opening cursor")
			defer cur.Close()
			var count int64
			for {
				_, _, err := cur.GetVal(nil, nil, NEXT)
				if err == NotFound {
					return
				}
				if err != nil {
					b.Fatalf("error getting data: %v", err)
				}
				count++
			}
			if count != benchDBNumKeys {
				b.Fatalf("unexpected number of keys: %d", count)
			}
		}()
	}
	b.StopTimer()
}

func setupBenchDB(b *testing.B) (*Env, string) {
	env, err := NewEnv()
	bMust(b, err, "creating env")
	err = env.SetMaxDBs(26)
	bMust(b, err, "setting max dbs")
	err = env.SetMapSize(1 << 30) // 1GB
	bMust(b, err, "sizing env")
	path, err := ioutil.TempDir("", "mdb_test-bench-")
	bMust(b, err, "creating temp directory")
	err = env.Open(path, 0, 0644)
	if err != nil {
		teardownBenchDB(b, env, path)
	}
	bMust(b, err, "opening database")
	return env, path
}

func openBenchDBI(b *testing.B, env *Env) DBI {
	txn, err := env.BeginTxn(nil, 0)
	bMust(b, err, "starting transaction")
	name := "benchmark"
	dbi, err := txn.DBIOpen(&name, CREATE)
	if err != nil {
		txn.Abort()
		b.Fatalf("error opening dbi: %v", err)
	}
	err = txn.Commit()
	bMust(b, err, "commiting transaction")
	return dbi
}

func teardownBenchDB(b *testing.B, env *Env, path string) {
	env.Close()
	os.RemoveAll(path)
}

func randBytes(n int) []byte {
	p := make([]byte, n)
	crand.Read(p)
	return p
}

func bMust(b *testing.B, err error, action string) {
	if err != nil {
		b.Fatalf("error %s: %v", action, err)
	}
}

func bTxnMust(b *testing.B, txn *Txn, err error, action string) {
	if err != nil {
		txn.Abort()
		b.Fatalf("error %s: %v", action, err)
	}
}

const randSourceSize = 500 << 20 // size of the 'entropy pool' for random byte generation.
const benchDBNumKeys = 100000    // number of keys to store in benchmark databases
const benchDBMaxKeyLen = 30      // maximum length for database keys (size is limited by MDB)
const benchDBMaxValLen = 2000    // maximum lengh for database values

func makeBenchDBKey(c *randSourceCursor) []byte {
	return c.NBytes(rand.Intn(benchDBMaxKeyLen) + 1)
}

func makeBenchDBVal(c *randSourceCursor) []byte {
	return c.NBytes(rand.Intn(benchDBMaxValLen) + 1)
}

// holds a bunch of random bytes so repeated generation af 'random' slices is
// cheap.  acts as a ring which can be read from (although doesn't implement io.Reader).
var randSource [randSourceSize]byte

func initRandSource(b *testing.B) {
	if randSource[0] == 0 && randSource[1] == 0 && randSource[2] == 0 && randSource[3] == 0 {
		b.Logf("initializing random source data")
		n, err := crand.Read(randSource[:])
		bMust(b, err, "initializing random source")
		if n < len(randSource) {
			b.Fatalf("unable to read enough random source data %d", n)
		}
	}
}

// acts as a simple byte slice generator.
type randSourceCursor int

func newRandSourceCursor() randSourceCursor {
	i := rand.Intn(randSourceSize)
	return randSourceCursor(i)
}

func (c *randSourceCursor) NBytes(n int) []byte {
	i := int(*c)
	if n >= randSourceSize {
		panic("rand size too big")
	}
	*c = (*c + randSourceCursor(n)) % randSourceSize
	_n := i + n - randSourceSize
	if _n > 0 {
		p := make([]byte, n)
		m := copy(p, randSource[i:])
		copy(p[m:], randSource[:])
		return p
	}
	return randSource[i : i+n]
}
<commit_msg>Remove unreachable code (go vet FTW)<commit_after>package mdb

import (
	crand "crypto/rand"
	"io/ioutil"
	"math/rand"
	"os"
	"testing"
)

// repeatedly put (overwrite) keys.
func BenchmarkTxnPut(b *testing.B) {
	initRandSource(b)
	env, path := setupBenchDB(b)
	defer teardownBenchDB(b, env, path)

	dbi := openBenchDBI(b, env)

	var ps [][]byte

	rc := newRandSourceCursor()
	txn, err := env.BeginTxn(nil, 0)
	bMust(b, err, "starting transaction")
	for i := 0; i < benchDBNumKeys; i++ {
		k := makeBenchDBKey(&rc)
		v := makeBenchDBVal(&rc)
		err := txn.Put(dbi, k, v, 0)
		ps = append(ps, k, v)
		bTxnMust(b, txn, err, "putting data")
	}
	err = txn.Commit()
	bMust(b, err, "commiting transaction")

	txn, err = env.BeginTxn(nil, 0)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		k := ps[rand.Intn(len(ps)/2)*2]
		v := makeBenchDBVal(&rc)
		err := txn.Put(dbi, k, v, 0)
		bTxnMust(b, txn, err, "putting data")
	}
	b.StopTimer()
	err = txn.Commit()
	bMust(b, err, "commiting transaction")
}

// repeatedly get random keys.
func BenchmarkTxnGetRDONLY(b *testing.B) {
	initRandSource(b)
	env, path := setupBenchDB(b)
	defer teardownBenchDB(b, env, path)

	dbi := openBenchDBI(b, env)

	var ps [][]byte

	rc := newRandSourceCursor()
	txn, err := env.BeginTxn(nil, 0)
	bMust(b, err, "starting transaction")
	for i := 0; i < benchDBNumKeys; i++ {
		k := makeBenchDBKey(&rc)
		v := makeBenchDBVal(&rc)
		err := txn.Put(dbi, k, v, 0)
		ps = append(ps, k, v)
		bTxnMust(b, txn, err, "putting data")
	}
	err = txn.Commit()
	bMust(b, err, "commiting transaction")

	txn, err = env.BeginTxn(nil, RDONLY)
	bMust(b, err, "starting transaction")
	defer txn.Abort()
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_, err := txn.Get(dbi, ps[rand.Intn(len(ps))])
		if err == NotFound {
			continue
		}
		if err != nil {
			b.Fatalf("error getting data: %v", err)
		}
	}
	b.StopTimer()
}

// like BenchmarkTxnGetRDONLY, but txn.GetVal() is called instead.
func BenchmarkTxnGetValRDONLY(b *testing.B) {
	initRandSource(b)
	env, path := setupBenchDB(b)
	defer teardownBenchDB(b, env, path)

	dbi := openBenchDBI(b, env)

	var ps [][]byte

	rc := newRandSourceCursor()
	txn, err := env.BeginTxn(nil, 0)
	bMust(b, err, "starting transaction")
	for i := 0; i < benchDBNumKeys; i++ {
		k := makeBenchDBKey(&rc)
		v := makeBenchDBVal(&rc)
		err := txn.Put(dbi, k, v, 0)
		ps = append(ps, k, v)
		bTxnMust(b, txn, err, "putting data")
	}
	err = txn.Commit()
	bMust(b, err, "commiting transaction")

	txn, err = env.BeginTxn(nil, RDONLY)
	bMust(b, err, "starting transaction")
	defer txn.Abort()
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_, err := txn.GetVal(dbi, ps[rand.Intn(len(ps))])
		if err == NotFound {
			continue
		}
		if err != nil {
			b.Fatalf("error getting data: %v", err)
		}
	}
	b.StopTimer()
}

// repeatedly scan all the values in a database.
func BenchmarkCursorScanRDONLY(b *testing.B) {
	initRandSource(b)
	env, path := setupBenchDB(b)
	defer teardownBenchDB(b, env, path)

	dbi := openBenchDBI(b, env)

	var ps [][]byte

	rc := newRandSourceCursor()
	txn, err := env.BeginTxn(nil, 0)
	bMust(b, err, "starting transaction")
	for i := 0; i < benchDBNumKeys; i++ {
		k := makeBenchDBKey(&rc)
		v := makeBenchDBVal(&rc)
		err := txn.Put(dbi, k, v, 0)
		ps = append(ps, k, v)
		bTxnMust(b, txn, err, "putting data")
	}
	err = txn.Commit()
	bMust(b, err, "commiting transaction")

	txn, err = env.BeginTxn(nil, RDONLY)
	bMust(b, err, "starting transaction")
	defer txn.Abort()
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		func() {
			cur, err := txn.CursorOpen(dbi)
			bMust(b, err, "opening cursor")
			defer cur.Close()
			var count int64
			for {
				_, _, err := cur.Get(nil, nil, NEXT)
				if err == NotFound {
					return
				}
				if err != nil {
					b.Fatalf("error getting data: %v", err)
				}
				count++
			}
		}()
	}
	b.StopTimer()
}

// like BenchmarkCursoreScanRDONLY, but cursor.GetVal() is called instead.
func BenchmarkCursorScanValRDONLY(b *testing.B) {
	initRandSource(b)
	env, path := setupBenchDB(b)
	defer teardownBenchDB(b, env, path)

	dbi := openBenchDBI(b, env)

	var ps [][]byte

	rc := newRandSourceCursor()
	txn, err := env.BeginTxn(nil, 0)
	bMust(b, err, "starting transaction")
	for i := 0; i < benchDBNumKeys; i++ {
		k := makeBenchDBKey(&rc)
		v := makeBenchDBVal(&rc)
		err := txn.Put(dbi, k, v, 0)
		ps = append(ps, k, v)
		bTxnMust(b, txn, err, "putting data")
	}
	err = txn.Commit()
	bMust(b, err, "commiting transaction")

	txn, err = env.BeginTxn(nil, RDONLY)
	bMust(b, err, "starting transaction")
	defer txn.Abort()
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		func() {
			cur, err := txn.CursorOpen(dbi)
			bMust(b, err, "opening cursor")
			defer cur.Close()
			var count int64
			for {
				_, _, err := cur.GetVal(nil, nil, NEXT)
				if err == NotFound {
					return
				}
				if err != nil {
					b.Fatalf("error getting data: %v", err)
				}
				count++
			}
		}()
	}
	b.StopTimer()
}

func setupBenchDB(b *testing.B) (*Env, string) {
	env, err := NewEnv()
	bMust(b, err, "creating env")
	err = env.SetMaxDBs(26)
	bMust(b, err, "setting max dbs")
	err = env.SetMapSize(1 << 30) // 1GB
	bMust(b, err, "sizing env")
	path, err := ioutil.TempDir("", "mdb_test-bench-")
	bMust(b, err, "creating temp directory")
	err = env.Open(path, 0, 0644)
	if err != nil {
		teardownBenchDB(b, env, path)
	}
	bMust(b, err, "opening database")
	return env, path
}

func openBenchDBI(b *testing.B, env *Env) DBI {
	txn, err := env.BeginTxn(nil, 0)
	bMust(b, err, "starting transaction")
	name := "benchmark"
	dbi, err := txn.DBIOpen(&name, CREATE)
	if err != nil {
		txn.Abort()
		b.Fatalf("error opening dbi: %v", err)
	}
	err = txn.Commit()
	bMust(b, err, "commiting transaction")
	return dbi
}

func teardownBenchDB(b *testing.B, env *Env, path string) {
	env.Close()
	os.RemoveAll(path)
}

func randBytes(n int) []byte {
	p := make([]byte, n)
	crand.Read(p)
	return p
}

func bMust(b *testing.B, err error, action string) {
	if err != nil {
		b.Fatalf("error %s: %v", action, err)
	}
}

func bTxnMust(b *testing.B, txn *Txn, err error, action string) {
	if err != nil {
		txn.Abort()
		b.Fatalf("error %s: %v", action, err)
	}
}

const randSourceSize = 500 << 20 // size of the 'entropy pool' for random byte generation.
const benchDBNumKeys = 100000    // number of keys to store in benchmark databases
const benchDBMaxKeyLen = 30      // maximum length for database keys (size is limited by MDB)
const benchDBMaxValLen = 2000    // maximum lengh for database values

func makeBenchDBKey(c *randSourceCursor) []byte {
	return c.NBytes(rand.Intn(benchDBMaxKeyLen) + 1)
}

func makeBenchDBVal(c *randSourceCursor) []byte {
	return c.NBytes(rand.Intn(benchDBMaxValLen) + 1)
}

// holds a bunch of random bytes so repeated generation af 'random' slices is
// cheap.  acts as a ring which can be read from (although doesn't implement io.Reader).
var randSource [randSourceSize]byte

func initRandSource(b *testing.B) {
	if randSource[0] == 0 && randSource[1] == 0 && randSource[2] == 0 && randSource[3] == 0 {
		b.Logf("initializing random source data")
		n, err := crand.Read(randSource[:])
		bMust(b, err, "initializing random source")
		if n < len(randSource) {
			b.Fatalf("unable to read enough random source data %d", n)
		}
	}
}

// acts as a simple byte slice generator.
type randSourceCursor int

func newRandSourceCursor() randSourceCursor {
	i := rand.Intn(randSourceSize)
	return randSourceCursor(i)
}

func (c *randSourceCursor) NBytes(n int) []byte {
	i := int(*c)
	if n >= randSourceSize {
		panic("rand size too big")
	}
	*c = (*c + randSourceCursor(n)) % randSourceSize
	_n := i + n - randSourceSize
	if _n > 0 {
		p := make([]byte, n)
		m := copy(p, randSource[i:])
		copy(p[m:], randSource[:])
		return p
	}
	return randSource[i : i+n]
}
<|endoftext|>


<commit_before>package command

import (
	"fmt"
	"io/ioutil"
	"os"
	"strings"
)

const (
	// DefaultInitName is the default name we use when
	// initializing the example file
	DefaultInitName = "example.nomad"
)

// JobInitCommand generates a new job template that you can customize to your
// liking, like vagrant init
type JobInitCommand struct {
	Meta
}

func (c *JobInitCommand) Help() string {
	helpText := `
Usage: nomad job init
Alias: nomad init

  Creates an example job file that can be used as a starting
  point to customize further.
`
	return strings.TrimSpace(helpText)
}

func (c *JobInitCommand) Synopsis() string {
	return "Create an example job file"
}

func (c *JobInitCommand) Run(args []string) int {
	// Check for misuse
	if len(args) != 0 {
		c.Ui.Error(c.Help())
		return 1
	}

	// Check if the file already exists
	_, err := os.Stat(DefaultInitName)
	if err != nil && !os.IsNotExist(err) {
		c.Ui.Error(fmt.Sprintf("Failed to stat '%s': %v", DefaultInitName, err))
		return 1
	}
	if !os.IsNotExist(err) {
		c.Ui.Error(fmt.Sprintf("Job '%s' already exists", DefaultInitName))
		return 1
	}

	// Write out the example
	err = ioutil.WriteFile(DefaultInitName, []byte(defaultJob), 0660)
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Failed to write '%s': %v", DefaultInitName, err))
		return 1
	}

	// Success
	c.Ui.Output(fmt.Sprintf("Example job file written to %s", DefaultInitName))
	return 0
}

var defaultJob = strings.TrimSpace(`
# There can only be a single job definition per file. This job is named
# "example" so it will create a job with the ID and Name "example".

# The "job" stanza is the top-most configuration option in the job
# specification. A job is a declarative specification of tasks that Nomad
# should run. Jobs have a globally unique name, one or many task groups, which
# are themselves collections of one or many tasks.
#
# For more information and examples on the "job" stanza, please see
# the online documentation at:
#
#     https://www.nomadproject.io/docs/job-specification/job.html
#
job "example" {
  # The "region" parameter specifies the region in which to execute the job. If
  # omitted, this inherits the default region name of "global".
  # region = "global"

  # The "datacenters" parameter specifies the list of datacenters which should
  # be considered when placing this task. This must be provided.
  datacenters = ["dc1"]

  # The "type" parameter controls the type of job, which impacts the scheduler's
  # decision on placement. This configuration is optional and defaults to
  # "service". For a full list of job types and their differences, please see
  # the online documentation.
  #
  # For more information, please see the online documentation at:
  #
  #     https://www.nomadproject.io/docs/jobspec/schedulers.html
  #
  type = "service"

  # The "constraint" stanza defines additional constraints for placing this job,
  # in addition to any resource or driver constraints. This stanza may be placed
  # at the "job", "group", or "task" level, and supports variable interpolation.
  #
  # For more information and examples on the "constraint" stanza, please see
  # the online documentation at:
  #
  #     https://www.nomadproject.io/docs/job-specification/constraint.html
  #
  # constraint {
  #   attribute = "${attr.kernel.name}"
  #   value     = "linux"
  # }

  # The "update" stanza specifies the update strategy of task groups. The update
  # strategy is used to control things like rolling upgrades, canaries, and
  # blue/green deployments. If omitted, no update strategy is enforced. The
  # "update" stanza may be placed at the job or task group. When placed at the
  # job, it applies to all groups within the job. When placed at both the job and
  # group level, the stanzas are merged with the group's taking precedence.
  #
  # For more information and examples on the "update" stanza, please see
  # the online documentation at:
  #
  #     https://www.nomadproject.io/docs/job-specification/update.html
  #
  update {
    # The "max_parallel" parameter specifies the maximum number of updates to
    # perform in parallel. In this case, this specifies to update a single task
    # at a time.
    max_parallel = 1

    # The "min_healthy_time" parameter specifies the minimum time the allocation
    # must be in the healthy state before it is marked as healthy and unblocks
    # further allocations from being updated.
    min_healthy_time = "10s"

    # The "healthy_deadline" parameter specifies the deadline in which the
    # allocation must be marked as healthy after which the allocation is
    # automatically transitioned to unhealthy. Transitioning to unhealthy will
    # fail the deployment and potentially roll back the job if "auto_revert" is
    # set to true.
    healthy_deadline = "3m"

    # The "auto_revert" parameter specifies if the job should auto-revert to the
    # last stable job on deployment failure. A job is marked as stable if all the
    # allocations as part of its deployment were marked healthy.
    auto_revert = false

    # The "canary" parameter specifies that changes to the job that would result
    # in destructive updates should create the specified number of canaries
    # without stopping any previous allocations. Once the operator determines the
    # canaries are healthy, they can be promoted which unblocks a rolling update
    # of the remaining allocations at a rate of "max_parallel".
    #
    # Further, setting "canary" equal to the count of the task group allows
    # blue/green deployments. When the job is updated, a full set of the new
    # version is deployed and upon promotion the old version is stopped.
    canary = 0
  }

  # The "group" stanza defines a series of tasks that should be co-located on
  # the same Nomad client. Any task within a group will be placed on the same
  # client.
  #
  # For more information and examples on the "group" stanza, please see
  # the online documentation at:
  #
  #     https://www.nomadproject.io/docs/job-specification/group.html
  #
  group "cache" {
    # The "count" parameter specifies the number of the task groups that should
    # be running under this group. This value must be non-negative and defaults
    # to 1.
    count = 1

    # The "restart" stanza configures a group's behavior on task failure. If
    # left unspecified, a default restart policy is used based on the job type.
    #
    # For more information and examples on the "restart" stanza, please see
    # the online documentation at:
    #
    #     https://www.nomadproject.io/docs/job-specification/restart.html
    #
    restart {
      # The number of attempts to run the job within the specified interval.
      attempts = 2
      interval = "30m"

      # The "delay" parameter specifies the duration to wait before restarting
      # a task after it has failed.
      delay = "15s"

     # The "mode" parameter controls what happens when a task has restarted
     # "attempts" times within the interval. "delay" mode delays the next
     # restart until the next interval. "fail" mode does not restart the task
     # if "attempts" has been hit within the interval.
      mode = "fail"
    }

    # The "ephemeral_disk" stanza instructs Nomad to utilize an ephemeral disk
    # instead of a hard disk requirement. Clients using this stanza should
    # not specify disk requirements in the resources stanza of the task. All
    # tasks in this group will share the same ephemeral disk.
    #
    # For more information and examples on the "ephemeral_disk" stanza, please
    # see the online documentation at:
    #
    #     https://www.nomadproject.io/docs/job-specification/ephemeral_disk.html
    #
    ephemeral_disk {
      # When sticky is true and the task group is updated, the scheduler
      # will prefer to place the updated allocation on the same node and
      # will migrate the data. This is useful for tasks that store data
      # that should persist across allocation updates.
      # sticky = true
      #
      # Setting migrate to true results in the allocation directory of a
      # sticky allocation directory to be migrated.
      # migrate = true

      # The "size" parameter specifies the size in MB of shared ephemeral disk
      # between tasks in the group.
      size = 300
    }

    # The "task" stanza creates an individual unit of work, such as a Docker
    # container, web application, or batch processing.
    #
    # For more information and examples on the "task" stanza, please see
    # the online documentation at:
    #
    #     https://www.nomadproject.io/docs/job-specification/task.html
    #
    task "redis" {
      # The "driver" parameter specifies the task driver that should be used to
      # run the task.
      driver = "docker"

      # The "config" stanza specifies the driver configuration, which is passed
      # directly to the driver to start the task. The details of configurations
      # are specific to each driver, so please see specific driver
      # documentation for more information.
      config {
        image = "redis:3.2"
        port_map {
          db = 6379
        }
      }

      # The "artifact" stanza instructs Nomad to download an artifact from a
      # remote source prior to starting the task. This provides a convenient
      # mechanism for downloading configuration files or data needed to run the
      # task. It is possible to specify the "artifact" stanza multiple times to
      # download multiple artifacts.
      #
      # For more information and examples on the "artifact" stanza, please see
      # the online documentation at:
      #
      #     https://www.nomadproject.io/docs/job-specification/artifact.html
      #
      # artifact {
      #   source = "http://foo.com/artifact.tar.gz"
      #   options {
      #     checksum = "md5:c4aa853ad2215426eb7d70a21922e794"
      #   }
      # }

      # The "logs" stanza instructs the Nomad client on how many log files and
      # the maximum size of those logs files to retain. Logging is enabled by
      # default, but the "logs" stanza allows for finer-grained control over
      # the log rotation and storage configuration.
      #
      # For more information and examples on the "logs" stanza, please see
      # the online documentation at:
      #
      #     https://www.nomadproject.io/docs/job-specification/logs.html
      #
      # logs {
      #   max_files     = 10
      #   max_file_size = 15
      # }

      # The "resources" stanza describes the requirements a task needs to
      # execute. Resource requirements include memory, network, cpu, and more.
      # This ensures the task will execute on a machine that contains enough
      # resource capacity.
      #
      # For more information and examples on the "resources" stanza, please see
      # the online documentation at:
      #
      #     https://www.nomadproject.io/docs/job-specification/resources.html
      #
      resources {
        cpu    = 500 # 500 MHz
        memory = 256 # 256MB
        network {
          mbits = 10
          port "db" {}
        }
      }

      # The "service" stanza instructs Nomad to register this task as a service
      # in the service discovery engine, which is currently Consul. This will
      # make the service addressable after Nomad has placed it on a host and
      # port.
      #
      # For more information and examples on the "service" stanza, please see
      # the online documentation at:
      #
      #     https://www.nomadproject.io/docs/job-specification/service.html
      #
      service {
        name = "redis-cache"
        tags = ["global", "cache"]
        port = "db"
        check {
          name     = "alive"
          type     = "tcp"
          interval = "10s"
          timeout  = "2s"
        }
      }

      # The "template" stanza instructs Nomad to manage a template, such as
      # a configuration file or script. This template can optionally pull data
      # from Consul or Vault to populate runtime configuration data.
      #
      # For more information and examples on the "template" stanza, please see
      # the online documentation at:
      #
      #     https://www.nomadproject.io/docs/job-specification/template.html
      #
      # template {
      #   data          = "---\nkey: {{ key \"service/my-key\" }}"
      #   destination   = "local/file.yml"
      #   change_mode   = "signal"
      #   change_signal = "SIGHUP"
      # }

      # The "template" stanza can also be used to create environment variables
      # for tasks that prefer those to config files. The task will be restarted
      # when data pulled from Consul or Vault changes.
      #
      # template {
      #   data        = "KEY={{ key \"service/my-key\" }}"
      #   destination = "local/file.env"
      #   env         = true
      # }

      # The "vault" stanza instructs the Nomad client to acquire a token from
      # a HashiCorp Vault server. The Nomad servers must be configured and
      # authorized to communicate with Vault. By default, Nomad will inject
      # The token into the job via an environment variable and make the token
      # available to the "template" stanza. The Nomad client handles the renewal
      # and revocation of the Vault token.
      #
      # For more information and examples on the "vault" stanza, please see
      # the online documentation at:
      #
      #     https://www.nomadproject.io/docs/job-specification/vault.html
      #
      # vault {
      #   policies      = ["cdn", "frontend"]
      #   change_mode   = "signal"
      #   change_signal = "SIGHUP"
      # }

      # Controls the timeout between signalling a task it will be killed
      # and killing the task. If not set a default is used.
      # kill_timeout = "20s"
    }
  }
}
`)
<commit_msg>docs: add migrate to example.nomad<commit_after>package command

import (
	"fmt"
	"io/ioutil"
	"os"
	"strings"
)

const (
	// DefaultInitName is the default name we use when
	// initializing the example file
	DefaultInitName = "example.nomad"
)

// JobInitCommand generates a new job template that you can customize to your
// liking, like vagrant init
type JobInitCommand struct {
	Meta
}

func (c *JobInitCommand) Help() string {
	helpText := `
Usage: nomad job init
Alias: nomad init

  Creates an example job file that can be used as a starting
  point to customize further.
`
	return strings.TrimSpace(helpText)
}

func (c *JobInitCommand) Synopsis() string {
	return "Create an example job file"
}

func (c *JobInitCommand) Run(args []string) int {
	// Check for misuse
	if len(args) != 0 {
		c.Ui.Error(c.Help())
		return 1
	}

	// Check if the file already exists
	_, err := os.Stat(DefaultInitName)
	if err != nil && !os.IsNotExist(err) {
		c.Ui.Error(fmt.Sprintf("Failed to stat '%s': %v", DefaultInitName, err))
		return 1
	}
	if !os.IsNotExist(err) {
		c.Ui.Error(fmt.Sprintf("Job '%s' already exists", DefaultInitName))
		return 1
	}

	// Write out the example
	err = ioutil.WriteFile(DefaultInitName, []byte(defaultJob), 0660)
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Failed to write '%s': %v", DefaultInitName, err))
		return 1
	}

	// Success
	c.Ui.Output(fmt.Sprintf("Example job file written to %s", DefaultInitName))
	return 0
}

var defaultJob = strings.TrimSpace(`
# There can only be a single job definition per file. This job is named
# "example" so it will create a job with the ID and Name "example".

# The "job" stanza is the top-most configuration option in the job
# specification. A job is a declarative specification of tasks that Nomad
# should run. Jobs have a globally unique name, one or many task groups, which
# are themselves collections of one or many tasks.
#
# For more information and examples on the "job" stanza, please see
# the online documentation at:
#
#     https://www.nomadproject.io/docs/job-specification/job.html
#
job "example" {
  # The "region" parameter specifies the region in which to execute the job. If
  # omitted, this inherits the default region name of "global".
  # region = "global"

  # The "datacenters" parameter specifies the list of datacenters which should
  # be considered when placing this task. This must be provided.
  datacenters = ["dc1"]

  # The "type" parameter controls the type of job, which impacts the scheduler's
  # decision on placement. This configuration is optional and defaults to
  # "service". For a full list of job types and their differences, please see
  # the online documentation.
  #
  # For more information, please see the online documentation at:
  #
  #     https://www.nomadproject.io/docs/jobspec/schedulers.html
  #
  type = "service"

  # The "constraint" stanza defines additional constraints for placing this job,
  # in addition to any resource or driver constraints. This stanza may be placed
  # at the "job", "group", or "task" level, and supports variable interpolation.
  #
  # For more information and examples on the "constraint" stanza, please see
  # the online documentation at:
  #
  #     https://www.nomadproject.io/docs/job-specification/constraint.html
  #
  # constraint {
  #   attribute = "${attr.kernel.name}"
  #   value     = "linux"
  # }

  # The "update" stanza specifies the update strategy of task groups. The update
  # strategy is used to control things like rolling upgrades, canaries, and
  # blue/green deployments. If omitted, no update strategy is enforced. The
  # "update" stanza may be placed at the job or task group. When placed at the
  # job, it applies to all groups within the job. When placed at both the job and
  # group level, the stanzas are merged with the group's taking precedence.
  #
  # For more information and examples on the "update" stanza, please see
  # the online documentation at:
  #
  #     https://www.nomadproject.io/docs/job-specification/update.html
  #
  update {
    # The "max_parallel" parameter specifies the maximum number of updates to
    # perform in parallel. In this case, this specifies to update a single task
    # at a time.
    max_parallel = 1

    # The "min_healthy_time" parameter specifies the minimum time the allocation
    # must be in the healthy state before it is marked as healthy and unblocks
    # further allocations from being updated.
    min_healthy_time = "10s"

    # The "healthy_deadline" parameter specifies the deadline in which the
    # allocation must be marked as healthy after which the allocation is
    # automatically transitioned to unhealthy. Transitioning to unhealthy will
    # fail the deployment and potentially roll back the job if "auto_revert" is
    # set to true.
    healthy_deadline = "3m"

    # The "auto_revert" parameter specifies if the job should auto-revert to the
    # last stable job on deployment failure. A job is marked as stable if all the
    # allocations as part of its deployment were marked healthy.
    auto_revert = false

    # The "canary" parameter specifies that changes to the job that would result
    # in destructive updates should create the specified number of canaries
    # without stopping any previous allocations. Once the operator determines the
    # canaries are healthy, they can be promoted which unblocks a rolling update
    # of the remaining allocations at a rate of "max_parallel".
    #
    # Further, setting "canary" equal to the count of the task group allows
    # blue/green deployments. When the job is updated, a full set of the new
    # version is deployed and upon promotion the old version is stopped.
    canary = 0
  }

  # The migrate stanza specifies the group's strategy for migrating off of
  # draining nodes. If omitted, a default migration strategy is applied.
  #
  # For more information on the "migrate" stanza, please see
  # the online documentation at:
  #
  #     https://www.nomadproject.io/docs/job-specification/migrate.html
  #
  migrate {
    # Specifies the number of task groups that can be migrated at the same
    # time. This number must be less than the total count for the group as
    # (count - max_parallel) will be left running during migrations.
    max_parallel = 1

    # Specifies the mechanism in which allocations health is determined. The
    # potential values are "checks" or "task_states".
    health_check = "checks"

    # Specifies the minimum time the allocation must be in the healthy state
    # before it is marked as healthy and unblocks further allocations from being
    # migrated. This is specified using a label suffix like "30s" or "15m".
    min_healthy_time = "10s"

    # Specifies the deadline in which the allocation must be marked as healthy
    # after which the allocation is automatically transitioned to unhealthy. This
    # is specified using a label suffix like "2m" or "1h".
    healthy_deadline = "5m"
  }

  # The "group" stanza defines a series of tasks that should be co-located on
  # the same Nomad client. Any task within a group will be placed on the same
  # client.
  #
  # For more information and examples on the "group" stanza, please see
  # the online documentation at:
  #
  #     https://www.nomadproject.io/docs/job-specification/group.html
  #
  group "cache" {
    # The "count" parameter specifies the number of the task groups that should
    # be running under this group. This value must be non-negative and defaults
    # to 1.
    count = 1

    # The "restart" stanza configures a group's behavior on task failure. If
    # left unspecified, a default restart policy is used based on the job type.
    #
    # For more information and examples on the "restart" stanza, please see
    # the online documentation at:
    #
    #     https://www.nomadproject.io/docs/job-specification/restart.html
    #
    restart {
      # The number of attempts to run the job within the specified interval.
      attempts = 2
      interval = "30m"

      # The "delay" parameter specifies the duration to wait before restarting
      # a task after it has failed.
      delay = "15s"

     # The "mode" parameter controls what happens when a task has restarted
     # "attempts" times within the interval. "delay" mode delays the next
     # restart until the next interval. "fail" mode does not restart the task
     # if "attempts" has been hit within the interval.
      mode = "fail"
    }

    # The "ephemeral_disk" stanza instructs Nomad to utilize an ephemeral disk
    # instead of a hard disk requirement. Clients using this stanza should
    # not specify disk requirements in the resources stanza of the task. All
    # tasks in this group will share the same ephemeral disk.
    #
    # For more information and examples on the "ephemeral_disk" stanza, please
    # see the online documentation at:
    #
    #     https://www.nomadproject.io/docs/job-specification/ephemeral_disk.html
    #
    ephemeral_disk {
      # When sticky is true and the task group is updated, the scheduler
      # will prefer to place the updated allocation on the same node and
      # will migrate the data. This is useful for tasks that store data
      # that should persist across allocation updates.
      # sticky = true
      #
      # Setting migrate to true results in the allocation directory of a
      # sticky allocation directory to be migrated.
      # migrate = true

      # The "size" parameter specifies the size in MB of shared ephemeral disk
      # between tasks in the group.
      size = 300
    }

    # The "task" stanza creates an individual unit of work, such as a Docker
    # container, web application, or batch processing.
    #
    # For more information and examples on the "task" stanza, please see
    # the online documentation at:
    #
    #     https://www.nomadproject.io/docs/job-specification/task.html
    #
    task "redis" {
      # The "driver" parameter specifies the task driver that should be used to
      # run the task.
      driver = "docker"

      # The "config" stanza specifies the driver configuration, which is passed
      # directly to the driver to start the task. The details of configurations
      # are specific to each driver, so please see specific driver
      # documentation for more information.
      config {
        image = "redis:3.2"
        port_map {
          db = 6379
        }
      }

      # The "artifact" stanza instructs Nomad to download an artifact from a
      # remote source prior to starting the task. This provides a convenient
      # mechanism for downloading configuration files or data needed to run the
      # task. It is possible to specify the "artifact" stanza multiple times to
      # download multiple artifacts.
      #
      # For more information and examples on the "artifact" stanza, please see
      # the online documentation at:
      #
      #     https://www.nomadproject.io/docs/job-specification/artifact.html
      #
      # artifact {
      #   source = "http://foo.com/artifact.tar.gz"
      #   options {
      #     checksum = "md5:c4aa853ad2215426eb7d70a21922e794"
      #   }
      # }

      # The "logs" stanza instructs the Nomad client on how many log files and
      # the maximum size of those logs files to retain. Logging is enabled by
      # default, but the "logs" stanza allows for finer-grained control over
      # the log rotation and storage configuration.
      #
      # For more information and examples on the "logs" stanza, please see
      # the online documentation at:
      #
      #     https://www.nomadproject.io/docs/job-specification/logs.html
      #
      # logs {
      #   max_files     = 10
      #   max_file_size = 15
      # }

      # The "resources" stanza describes the requirements a task needs to
      # execute. Resource requirements include memory, network, cpu, and more.
      # This ensures the task will execute on a machine that contains enough
      # resource capacity.
      #
      # For more information and examples on the "resources" stanza, please see
      # the online documentation at:
      #
      #     https://www.nomadproject.io/docs/job-specification/resources.html
      #
      resources {
        cpu    = 500 # 500 MHz
        memory = 256 # 256MB
        network {
          mbits = 10
          port "db" {}
        }
      }

      # The "service" stanza instructs Nomad to register this task as a service
      # in the service discovery engine, which is currently Consul. This will
      # make the service addressable after Nomad has placed it on a host and
      # port.
      #
      # For more information and examples on the "service" stanza, please see
      # the online documentation at:
      #
      #     https://www.nomadproject.io/docs/job-specification/service.html
      #
      service {
        name = "redis-cache"
        tags = ["global", "cache"]
        port = "db"
        check {
          name     = "alive"
          type     = "tcp"
          interval = "10s"
          timeout  = "2s"
        }
      }

      # The "template" stanza instructs Nomad to manage a template, such as
      # a configuration file or script. This template can optionally pull data
      # from Consul or Vault to populate runtime configuration data.
      #
      # For more information and examples on the "template" stanza, please see
      # the online documentation at:
      #
      #     https://www.nomadproject.io/docs/job-specification/template.html
      #
      # template {
      #   data          = "---\nkey: {{ key \"service/my-key\" }}"
      #   destination   = "local/file.yml"
      #   change_mode   = "signal"
      #   change_signal = "SIGHUP"
      # }

      # The "template" stanza can also be used to create environment variables
      # for tasks that prefer those to config files. The task will be restarted
      # when data pulled from Consul or Vault changes.
      #
      # template {
      #   data        = "KEY={{ key \"service/my-key\" }}"
      #   destination = "local/file.env"
      #   env         = true
      # }

      # The "vault" stanza instructs the Nomad client to acquire a token from
      # a HashiCorp Vault server. The Nomad servers must be configured and
      # authorized to communicate with Vault. By default, Nomad will inject
      # The token into the job via an environment variable and make the token
      # available to the "template" stanza. The Nomad client handles the renewal
      # and revocation of the Vault token.
      #
      # For more information and examples on the "vault" stanza, please see
      # the online documentation at:
      #
      #     https://www.nomadproject.io/docs/job-specification/vault.html
      #
      # vault {
      #   policies      = ["cdn", "frontend"]
      #   change_mode   = "signal"
      #   change_signal = "SIGHUP"
      # }

      # Controls the timeout between signalling a task it will be killed
      # and killing the task. If not set a default is used.
      # kill_timeout = "20s"
    }
  }
}
`)
<|endoftext|>


<commit_before>package reviewdog

import (
	"fmt"
	"io"
	"strings"
)

var _ CommentService = &CommentWriter{}

type CommentWriter struct {
	w io.Writer
}

func NewCommentWriter(w io.Writer) *CommentWriter {
	return &CommentWriter{w: w}
}

func (s *CommentWriter) Post(c *Comment) error {
	_, err := fmt.Fprintln(s.w, strings.Join(c.CheckResult.Lines, "\n"))
	return err
}
<commit_msg>introduce UnifiedCommentWriter<commit_after>package reviewdog

import (
	"fmt"
	"io"
	"strings"
)

var _ CommentService = &CommentWriter{}

type CommentWriter struct {
	w io.Writer
}

func NewCommentWriter(w io.Writer) *CommentWriter {
	return &CommentWriter{w: w}
}

func (s *CommentWriter) Post(c *Comment) error {
	_, err := fmt.Fprintln(s.w, strings.Join(c.CheckResult.Lines, "\n"))
	return err
}

var _ CommentService = &UnifiedCommentWriter{}

// UnifiedCommentWriter is comment writer which writes results to given writer
// in one of following unified formats.
//
// Format:
//   - <file>: [<tool name>] <message>
//   - <file>:<lnum>: [<tool name>] <message>
//   - <file>:<lnum>:<col>: [<tool name>] <message>
// where <message> can be multiple lines.
type UnifiedCommentWriter struct {
	w io.Writer
}

func NewUnifiedCommentWriter(w io.Writer) *UnifiedCommentWriter {
	return &UnifiedCommentWriter{w: w}
}

func (mc *UnifiedCommentWriter) Post(c *Comment) error {
	s := c.Path
	if c.Lnum > 0 {
		s += fmt.Sprintf(":%d", c.Lnum)
		if c.Col > 0 {
			s += fmt.Sprintf(":%d", c.Col)
		}
	}
	s += fmt.Sprintf(": [%s] %s", c.ToolName, c.Body)
	_, err := fmt.Fprintln(mc.w, s)
	return err
}
<|endoftext|>


<commit_before>package varlink

import (
	"bufio"
	"encoding/json"
	"net"
	"strings"
)

// Error is a varlink error returned from a method call.
type Error struct {
	Name       string
	Parameters interface{}
}

// Error returns the fully-qualified varlink error name.
func (e *Error) Error() string {
	return e.Name
}

// Connection is a connection from a client to a service.
type Connection struct {
	address string
	conn    net.Conn
	reader  *bufio.Reader
	writer  *bufio.Writer
}

// Send sends a method call.
func (c *Connection) Send(method string, parameters interface{}, more bool, oneway bool) error {
	type call struct {
		Method     string      `json:"method"`
		Parameters interface{} `json:"parameters,omitempty"`
		More       bool        `json:"more,omitempty"`
		Oneway     bool        `json:"oneway,omitempty"`
	}

	if more && oneway {
		return &Error{
			Name:       "org.varlink.InvalidParameter",
			Parameters: "oneway",
		}
	}

	m := call{
		Method:     method,
		Parameters: parameters,
		More:       more,
		Oneway:     oneway,
	}
	b, err := json.Marshal(m)
	if err != nil {
		return err
	}

	b = append(b, 0)
	_, err = c.writer.Write(b)
	if err != nil {
		return err
	}

	return c.writer.Flush()
}

// Receive receives a method reply.
func (c *Connection) Receive(parameters interface{}) (bool, error) {
	type reply struct {
		Parameters *json.RawMessage `json:"parameters"`
		Continues  bool             `json:"continues"`
		Error      string           `json:"error"`
	}

	out, err := c.reader.ReadBytes('\x00')
	if err != nil {
		return false, err
	}

	var m reply
	err = json.Unmarshal(out[:len(out)-1], &m)
	if err != nil {
		return false, err
	}

	if m.Error != "" {
		return false, &Error{
			Name:       m.Error,
			Parameters: m.Parameters,
		}
	}

	if parameters != nil && m.Parameters != nil {
		return m.Continues, json.Unmarshal(*m.Parameters, parameters)
	}

	return m.Continues, nil
}

// Call sends a method call and returns the result of the call.
func (c *Connection) Call(method string, parameters interface{}, result interface{}) error {
	err := c.Send(method, &parameters, false, false)
	if err != nil {
		return err
	}

	_, err = c.Receive(result)
	return err
}

// GetInterfaceDescription requests the interface description string from the service.
func (c *Connection) GetInterfaceDescription(name string) (string, error) {
	type request struct {
		Interface string `json:"interface"`
	}
	type reply struct {
		Description string `json:"description"`
	}

	var r reply
	err := c.Call("org.varlink.service.GetInterfaceDescription", request{Interface: name}, &r)
	if err != nil {
		return "", err
	}

	return r.Description, nil
}

// GetInfo requests information about the service.
func (c *Connection) GetInfo(vendor *string, product *string, version *string, url *string, interfaces *[]string) error {
	type reply struct {
		Vendor     string   `json:"vendor"`
		Product    string   `json:"product"`
		Version    string   `json:"version"`
		URL        string   `json:"url"`
		Interfaces []string `json:"interfaces"`
	}

	var r reply
	err := c.Call("org.varlink.service.GetInfo", nil, &r)
	if err != nil {
		return err
	}

	if vendor != nil {
		*vendor = r.Vendor
	}
	if product != nil {
		*product = r.Product
	}
	if version != nil {
		*version = r.Version
	}
	if url != nil {
		*url = r.URL
	}
	if interfaces != nil {
		*interfaces = r.Interfaces
	}

	return nil
}

// Close terminates the connection.
func (c *Connection) Close() error {
	return c.conn.Close()
}

// NewConnection returns a new connection to the given address.
func NewConnection(address string) (*Connection, error) {
	var err error

	words := strings.SplitN(address, ":", 2)
	protocol := words[0]
	addr := words[1]

	// Ignore parameters after ';'
	words = strings.SplitN(addr, ";", 2)
	if words != nil {
		addr = words[0]
	}

	switch protocol {
	case "unix":
		break

	case "tcp":
		break
	}

	c := Connection{}
	c.conn, err = net.Dial(protocol, addr)
	if err != nil {
		return nil, err
	}

	c.address = address
	c.reader = bufio.NewReader(c.conn)
	c.writer = bufio.NewWriter(c.conn)

	return &c, nil
}
<commit_msg>Update "varlink" module<commit_after>package varlink

import (
	"bufio"
	"encoding/json"
	"net"
	"strings"
)

// Message flags for Send(). More indicates that the client accepts more than one method
// reply to this call. Oneway requests, that the service must not send a method reply to
// this call. Continues indicates that the service will send more than one reply.
const (
	More      = 1 << iota
	Oneway    = 1 << iota
	Continues = 1 << iota
)

// Error is a varlink error returned from a method call.
type Error struct {
	Name       string
	Parameters interface{}
}

// Error returns the fully-qualified varlink error name.
func (e *Error) Error() string {
	return e.Name
}

// Connection is a connection from a client to a service.
type Connection struct {
	address string
	conn    net.Conn
	reader  *bufio.Reader
	writer  *bufio.Writer
}

// Send sends a method call. It returns a receive() function which is called to retrieve the method reply.
// If Send() is called with the `More`flag and the receive() function carries the `Continues` flag, receive()
// can be called multiple times to retrieve multiple replies.
func (c *Connection) Send(method string, parameters interface{}, flags uint64) (func(interface{}) (uint64, error), error) {
	type call struct {
		Method     string      `json:"method"`
		Parameters interface{} `json:"parameters,omitempty"`
		More       bool        `json:"more,omitempty"`
		Oneway     bool        `json:"oneway,omitempty"`
	}

	if (flags&More != 0) && (flags&Oneway != 0) {
		return nil, &Error{
			Name:       "org.varlink.InvalidParameter",
			Parameters: "oneway",
		}
	}

	m := call{
		Method:     method,
		Parameters: parameters,
		More:       flags&More != 0,
		Oneway:     flags&Oneway != 0,
	}
	b, err := json.Marshal(m)
	if err != nil {
		return nil, err
	}

	b = append(b, 0)
	_, err = c.writer.Write(b)
	if err != nil {
		return nil, err
	}

	err = c.writer.Flush()
	if err != nil {
		return nil, err
	}

	receive := func(out_parameters interface{}) (uint64, error) {
		type reply struct {
			Parameters *json.RawMessage `json:"parameters"`
			Continues  bool             `json:"continues"`
			Error      string           `json:"error"`
		}

		out, err := c.reader.ReadBytes('\x00')
		if err != nil {
			return 0, err
		}

		var m reply
		err = json.Unmarshal(out[:len(out)-1], &m)
		if err != nil {
			return 0, err
		}

		if m.Error != "" {
			err = &Error{
				Name:       m.Error,
				Parameters: m.Parameters,
			}
			return 0, err
		}

		if m.Parameters != nil {
			json.Unmarshal(*m.Parameters, out_parameters)
		}

		if m.Continues {
			return Continues, nil
		}

		return 0, nil
	}

	return receive, nil
}

// Call sends a method call and returns the method reply.
func (c *Connection) Call(method string, parameters interface{}, out_parameters interface{}) error {
	receive, err := c.Send(method, &parameters, 0)
	if err != nil {
		return err
	}

	_, err = receive(out_parameters)
	return err
}

// GetInterfaceDescription requests the interface description string from the service.
func (c *Connection) GetInterfaceDescription(name string) (string, error) {
	type request struct {
		Interface string `json:"interface"`
	}
	type reply struct {
		Description string `json:"description"`
	}

	var r reply
	err := c.Call("org.varlink.service.GetInterfaceDescription", request{Interface: name}, &r)
	if err != nil {
		return "", err
	}

	return r.Description, nil
}

// GetInfo requests information about the service.
func (c *Connection) GetInfo(vendor *string, product *string, version *string, url *string, interfaces *[]string) error {
	type reply struct {
		Vendor     string   `json:"vendor"`
		Product    string   `json:"product"`
		Version    string   `json:"version"`
		URL        string   `json:"url"`
		Interfaces []string `json:"interfaces"`
	}

	var r reply
	err := c.Call("org.varlink.service.GetInfo", nil, &r)
	if err != nil {
		return err
	}

	if vendor != nil {
		*vendor = r.Vendor
	}
	if product != nil {
		*product = r.Product
	}
	if version != nil {
		*version = r.Version
	}
	if url != nil {
		*url = r.URL
	}
	if interfaces != nil {
		*interfaces = r.Interfaces
	}

	return nil
}

// Close terminates the connection.
func (c *Connection) Close() error {
	return c.conn.Close()
}

// NewConnection returns a new connection to the given address.
func NewConnection(address string) (*Connection, error) {
	var err error

	words := strings.SplitN(address, ":", 2)
	protocol := words[0]
	addr := words[1]

	// Ignore parameters after ';'
	words = strings.SplitN(addr, ";", 2)
	if words != nil {
		addr = words[0]
	}

	switch protocol {
	case "unix":
		break

	case "tcp":
		break
	}

	c := Connection{}
	c.conn, err = net.Dial(protocol, addr)
	if err != nil {
		return nil, err
	}

	c.address = address
	c.reader = bufio.NewReader(c.conn)
	c.writer = bufio.NewWriter(c.conn)

	return &c, nil
}
<|endoftext|>