<commit_before>package command

import (
	"crypto/sha1"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"time"

	"github.com/mackerelio/mackerel-agent/agent"
	"github.com/mackerelio/mackerel-agent/config"
	"github.com/mackerelio/mackerel-agent/logging"
	"github.com/mackerelio/mackerel-agent/mackerel"
	"github.com/mackerelio/mackerel-agent/spec"
)

var logger = logging.GetLogger("command")

const idFileName = "id"

func IdFilePath(root string) string {
	return filepath.Join(root, idFileName)
}

func LoadHostId(root string) (string, error) {
	content, err := ioutil.ReadFile(IdFilePath(root))
	if err != nil {
		return "", err
	}
	return string(content), nil
}

func SaveHostId(root string, id string) error {
	err := os.MkdirAll(root, 0755)
	if err != nil {
		return err
	}

	file, err := os.Create(IdFilePath(root))
	if err != nil {
		return err
	}
	defer file.Close()

	_, err = file.Write([]byte(id))
	if err != nil {
		return err
	}

	return nil
}

// prepareHost collects specs of the host and sends them to Mackerel server.
// A unique host-id is returned by the server if one is not specified.
func prepareHost(root string, api *mackerel.API, roleFullnames []string) (*mackerel.Host, error) {
	// XXX this configuration should be moved to under spec/linux
	os.Setenv("PATH", "/sbin:/usr/sbin:/bin:/usr/bin:"+os.Getenv("PATH"))
	os.Setenv("LANG", "C") // prevent changing outputs of some command, e.g. ifconfig.

	hostname, meta, interfaces, err := collectHostSpecs()
	if err != nil {
		return nil, fmt.Errorf("error while collecting host specs: %s", err.Error())
	}

	var result *mackerel.Host
	if hostId, err := LoadHostId(root); err != nil { // create
		logger.Debugf("Registering new host on mackerel...")
		createdHostId, err := api.CreateHost(hostname, meta, interfaces, roleFullnames)
		if err != nil {
			return nil, fmt.Errorf("Failed to register this host: %s", err.Error())
		}

		result, err = api.FindHost(createdHostId)
		if err != nil {
			return nil, fmt.Errorf("Failed to find this host on mackerel: %s", err.Error())
		}
	} else { // update
		result, err = api.FindHost(hostId)
		if err != nil {
			return nil, fmt.Errorf("Failed to find this host on mackerel (You may want to delete file \"%s\" to register this host to an another organization): %s", IdFilePath(root), err.Error())
		}
		err := api.UpdateHost(hostId, hostname, meta, interfaces, roleFullnames)
		if err != nil {
			return nil, fmt.Errorf("Failed to update this host: %s", err.Error())
		}
	}

	err = SaveHostId(root, result.Id)
	if err != nil {
		return nil, fmt.Errorf("Failed to save host ID: %s", err.Error())
	}

	return result, nil
}

// Interval between each updating host specs.
var specsUpdateInterval = 1 * time.Hour

func delayByHost(host *mackerel.Host) int {
	s := sha1.Sum([]byte(host.Id))
	return int(s[len(s)-1]) % int(config.PostMetricsInterval.Seconds())
}

type postValue struct {
	values   []*mackerel.CreatingMetricsValue
	retryCnt int
}

func newPostValue(values []*mackerel.CreatingMetricsValue) *postValue {
	return &postValue{values, 0}
}

type loopState uint8

const (
	loopStateFirst loopState = iota
	loopStateDefault
	loopStateQueued
	loopStateHadError
	loopStateTerminating
)

func loop(ag *agent.Agent, conf *config.Config, api *mackerel.API, host *mackerel.Host, termCh chan struct{}) int {
	quit := make(chan struct{})

	// Periodically update host specs.
	go func() {
	updateHostLoop:
		for {
			select {
			case <-quit:
				break updateHostLoop
			case <-time.After(specsUpdateInterval):
				UpdateHostSpecs(conf, api, host)
			}
		}
	}()

	metricsResult := ag.Watch()
	postQueue := make(chan *postValue, conf.Connection.Post_Metrics_Buffer_Size)
	go func() {
	enqueueLoop:
		for {
			select {
			case <-quit:
				break enqueueLoop
			case result := <-metricsResult:
				created := float64(result.Created.Unix())
				creatingValues := [](*mackerel.CreatingMetricsValue){}
				for name, value := range (map[string]float64)(result.Values) {
					creatingValues = append(
						creatingValues,
						&mackerel.CreatingMetricsValue{host.Id, name, created, value},
					)
				}
				logger.Debugf("Enqueuing task to post metrics.")
				postQueue <- newPostValue(creatingValues)
			}
		}
	}()

	return func() int {
		defer close(quit) // broadcast terminating
		postDelaySeconds := delayByHost(host)
		lState := loopStateFirst
		for {
			select {
			case <-termCh:
				if lState == loopStateTerminating {
					return 1
				}
				lState = loopStateTerminating
				if len(postQueue) <= 0 {
					return 0
				}
			case v := <-postQueue:
				origPostValues := [](*postValue){v}
				if len(postQueue) > 0 {
					// Bulk posting. However at most "two" metrics are to be posted, so postQueue isn't always empty yet.
					logger.Debugf("Merging datapoints with next queued ones")
					nextValues := <-postQueue
					origPostValues = append(origPostValues, nextValues)
				}

				delaySeconds := 0
				switch lState {
				case loopStateFirst: // request immediately to create graph defs of host
					// nop
				case loopStateQueued:
					delaySeconds = conf.Connection.Post_Metrics_Dequeue_Delay_Seconds
				case loopStateHadError:
					// TODO: better interval calculation. exponential backoff or so.
					delaySeconds = conf.Connection.Post_Metrics_Retry_Delay_Seconds
				case loopStateTerminating:
					// dequeue and post every one second when terminating.
					delaySeconds = 1
				default:
					// Sending data at every 0 second from all hosts causes request flooding.
					// To prevent flooding, this loop sleeps for some seconds
					// which is specific to the ID of the host running agent on.
					// The sleep second is up to 60s (to be exact up to `config.Postmetricsinterval.Seconds()`.
					elapsedSeconds := int(time.Now().Unix() % int64(config.PostMetricsInterval.Seconds()))
					if postDelaySeconds > elapsedSeconds {
						delaySeconds = postDelaySeconds - elapsedSeconds
					}
				}

				// determin next loopState before sleeping
				if lState != loopStateTerminating {
					if len(postQueue) > 0 {
						lState = loopStateQueued
					} else {
						lState = loopStateDefault
					}
				}

				logger.Debugf("Sleep %d seconds before posting.", delaySeconds)
				select {
				case <-time.After(time.Duration(delaySeconds) * time.Second):
					// nop
				case <-termCh:
					if lState == loopStateTerminating {
						return 1
					}
					lState = loopStateTerminating
				}

				postValues := [](*mackerel.CreatingMetricsValue){}
				for _, v := range origPostValues {
					postValues = append(postValues, v.values...)
				}
				err := api.PostMetricsValues(postValues)
				if err != nil {
					logger.Errorf("Failed to post metrics value (will retry): %s", err.Error())
					if lState != loopStateTerminating {
						lState = loopStateHadError
					}
					go func() {
						for _, v := range origPostValues {
							v.retryCnt++
							// It is difficult to distinguish the error is server error or data error.
							// So, if retryCnt exceeded the configured limit, postValue is considered invalid and abandoned.
							if v.retryCnt > conf.Connection.Post_Metrics_Retry_Max {
								json, err := json.Marshal(v.values)
								if err != nil {
									logger.Errorf("Something wrong with post values. marshaling failed.")
								} else {
									logger.Errorf("Post values may be invalid and abandoned: %s", string(json))
								}
								continue
							}
							postQueue <- v
						}
					}()
					continue
				}
				logger.Debugf("Posting metrics succeeded.")

				if lState == loopStateTerminating && len(postQueue) <= 0 {
					return 0
				}
			}
		}
	}()
}

// collectHostSpecs collects host specs (correspond to "name", "meta" and "interfaces" fields in API v0)
func collectHostSpecs() (string, map[string]interface{}, []map[string]interface{}, error) {
	hostname, err := os.Hostname()
	if err != nil {
		return "", nil, nil, fmt.Errorf("failed to obtain hostname: %s", err.Error())
	}

	meta := spec.Collect(specGenerators())

	interfacesSpec, err := interfaceGenerator().Generate()
	if err != nil {
		return "", nil, nil, fmt.Errorf("failed to collect interfaces: %s", err.Error())
	}

	interfaces, _ := interfacesSpec.([]map[string]interface{})

	return hostname, meta, interfaces, nil
}

// UpdateHostSpecs updates the host information that is already registered on Mackerel.
func UpdateHostSpecs(conf *config.Config, api *mackerel.API, host *mackerel.Host) {
	logger.Debugf("Updating host specs...")

	hostname, meta, interfaces, err := collectHostSpecs()
	if err != nil {
		logger.Errorf("While collecting host specs: %s", err)
		return
	}

	err = api.UpdateHost(host.Id, hostname, meta, interfaces, conf.Roles)
	if err != nil {
		logger.Errorf("Error while updating host specs: %s", err)
	} else {
		logger.Debugf("Host specs sent.")
	}
}

// Prepare sets up API and registers the host data to the Mackerel server.
// Use returned values to call Run().
func Prepare(conf *config.Config) (*mackerel.API, *mackerel.Host, error) {
	api, err := mackerel.NewApi(conf.Apibase, conf.Apikey, conf.Verbose)
	if err != nil {
		return nil, nil, fmt.Errorf("Failed to prepare an api: %s", err.Error())
	}

	host, err := prepareHost(conf.Root, api, conf.Roles)
	if err != nil {
		return nil, nil, fmt.Errorf("Failed to preapre host: %s", err.Error())
	}

	return api, host, nil
}

// Run starts the main metric collecting logic and this function will never return.
func Run(conf *config.Config, api *mackerel.API, host *mackerel.Host, termCh chan struct{}) int {
	logger.Infof("Start: apibase = %s, hostName = %s, hostId = %s", conf.Apibase, host.Name, host.Id)

	ag := &agent.Agent{
		MetricsGenerators: metricsGenerators(conf),
		PluginGenerators:  pluginGenerators(conf),
	}
	ag.InitPluginGenerators(api)

	return loop(ag, conf, api, host, termCh)
}
<commit_msg>wait up to 30 seconds before initial posting on start up<commit_after>package command

import (
	"crypto/sha1"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"time"

	"github.com/mackerelio/mackerel-agent/agent"
	"github.com/mackerelio/mackerel-agent/config"
	"github.com/mackerelio/mackerel-agent/logging"
	"github.com/mackerelio/mackerel-agent/mackerel"
	"github.com/mackerelio/mackerel-agent/spec"
)

var logger = logging.GetLogger("command")

const idFileName = "id"

func IdFilePath(root string) string {
	return filepath.Join(root, idFileName)
}

func LoadHostId(root string) (string, error) {
	content, err := ioutil.ReadFile(IdFilePath(root))
	if err != nil {
		return "", err
	}
	return string(content), nil
}

func SaveHostId(root string, id string) error {
	err := os.MkdirAll(root, 0755)
	if err != nil {
		return err
	}

	file, err := os.Create(IdFilePath(root))
	if err != nil {
		return err
	}
	defer file.Close()

	_, err = file.Write([]byte(id))
	if err != nil {
		return err
	}

	return nil
}

// prepareHost collects specs of the host and sends them to Mackerel server.
// A unique host-id is returned by the server if one is not specified.
func prepareHost(root string, api *mackerel.API, roleFullnames []string) (*mackerel.Host, error) {
	// XXX this configuration should be moved to under spec/linux
	os.Setenv("PATH", "/sbin:/usr/sbin:/bin:/usr/bin:"+os.Getenv("PATH"))
	os.Setenv("LANG", "C") // prevent changing outputs of some command, e.g. ifconfig.

	hostname, meta, interfaces, err := collectHostSpecs()
	if err != nil {
		return nil, fmt.Errorf("error while collecting host specs: %s", err.Error())
	}

	var result *mackerel.Host
	if hostId, err := LoadHostId(root); err != nil { // create
		logger.Debugf("Registering new host on mackerel...")
		createdHostId, err := api.CreateHost(hostname, meta, interfaces, roleFullnames)
		if err != nil {
			return nil, fmt.Errorf("Failed to register this host: %s", err.Error())
		}

		result, err = api.FindHost(createdHostId)
		if err != nil {
			return nil, fmt.Errorf("Failed to find this host on mackerel: %s", err.Error())
		}
	} else { // update
		result, err = api.FindHost(hostId)
		if err != nil {
			return nil, fmt.Errorf("Failed to find this host on mackerel (You may want to delete file \"%s\" to register this host to an another organization): %s", IdFilePath(root), err.Error())
		}
		err := api.UpdateHost(hostId, hostname, meta, interfaces, roleFullnames)
		if err != nil {
			return nil, fmt.Errorf("Failed to update this host: %s", err.Error())
		}
	}

	err = SaveHostId(root, result.Id)
	if err != nil {
		return nil, fmt.Errorf("Failed to save host ID: %s", err.Error())
	}

	return result, nil
}

// Interval between each updating host specs.
var specsUpdateInterval = 1 * time.Hour

func delayByHost(host *mackerel.Host) int {
	s := sha1.Sum([]byte(host.Id))
	return int(s[len(s)-1]) % int(config.PostMetricsInterval.Seconds())
}

type postValue struct {
	values   []*mackerel.CreatingMetricsValue
	retryCnt int
}

func newPostValue(values []*mackerel.CreatingMetricsValue) *postValue {
	return &postValue{values, 0}
}

type loopState uint8

const (
	loopStateFirst loopState = iota
	loopStateDefault
	loopStateQueued
	loopStateHadError
	loopStateTerminating
)

func loop(ag *agent.Agent, conf *config.Config, api *mackerel.API, host *mackerel.Host, termCh chan struct{}) int {
	quit := make(chan struct{})

	// Periodically update host specs.
	go func() {
	updateHostLoop:
		for {
			select {
			case <-quit:
				break updateHostLoop
			case <-time.After(specsUpdateInterval):
				UpdateHostSpecs(conf, api, host)
			}
		}
	}()

	metricsResult := ag.Watch()
	postQueue := make(chan *postValue, conf.Connection.Post_Metrics_Buffer_Size)
	go func() {
	enqueueLoop:
		for {
			select {
			case <-quit:
				break enqueueLoop
			case result := <-metricsResult:
				created := float64(result.Created.Unix())
				creatingValues := [](*mackerel.CreatingMetricsValue){}
				for name, value := range (map[string]float64)(result.Values) {
					creatingValues = append(
						creatingValues,
						&mackerel.CreatingMetricsValue{host.Id, name, created, value},
					)
				}
				logger.Debugf("Enqueuing task to post metrics.")
				postQueue <- newPostValue(creatingValues)
			}
		}
	}()

	return func() int {
		defer close(quit) // broadcast terminating
		postDelaySeconds := delayByHost(host)
		lState := loopStateFirst
		for {
			select {
			case <-termCh:
				if lState == loopStateTerminating {
					return 1
				}
				lState = loopStateTerminating
				if len(postQueue) <= 0 {
					return 0
				}
			case v := <-postQueue:
				origPostValues := [](*postValue){v}
				if len(postQueue) > 0 {
					// Bulk posting. However at most "two" metrics are to be posted, so postQueue isn't always empty yet.
					logger.Debugf("Merging datapoints with next queued ones")
					nextValues := <-postQueue
					origPostValues = append(origPostValues, nextValues)
				}

				delaySeconds := 0
				switch lState {
				case loopStateFirst: // request immediately to create graph defs of host
					// nop
				case loopStateQueued:
					delaySeconds = conf.Connection.Post_Metrics_Dequeue_Delay_Seconds
				case loopStateHadError:
					// TODO: better interval calculation. exponential backoff or so.
					delaySeconds = conf.Connection.Post_Metrics_Retry_Delay_Seconds
				case loopStateTerminating:
					// dequeue and post every one second when terminating.
					delaySeconds = 1
				default:
					// Sending data at every 0 second from all hosts causes request flooding.
					// To prevent flooding, this loop sleeps for some seconds
					// which is specific to the ID of the host running agent on.
					// The sleep second is up to 60s (to be exact up to `config.Postmetricsinterval.Seconds()`.
					elapsedSeconds := int(time.Now().Unix() % int64(config.PostMetricsInterval.Seconds()))
					if postDelaySeconds > elapsedSeconds {
						delaySeconds = postDelaySeconds - elapsedSeconds
					}
				}

				// determin next loopState before sleeping
				if lState != loopStateTerminating {
					if len(postQueue) > 0 {
						lState = loopStateQueued
					} else {
						lState = loopStateDefault
					}
				}

				logger.Debugf("Sleep %d seconds before posting.", delaySeconds)
				select {
				case <-time.After(time.Duration(delaySeconds) * time.Second):
					// nop
				case <-termCh:
					if lState == loopStateTerminating {
						return 1
					}
					lState = loopStateTerminating
				}

				postValues := [](*mackerel.CreatingMetricsValue){}
				for _, v := range origPostValues {
					postValues = append(postValues, v.values...)
				}
				err := api.PostMetricsValues(postValues)
				if err != nil {
					logger.Errorf("Failed to post metrics value (will retry): %s", err.Error())
					if lState != loopStateTerminating {
						lState = loopStateHadError
					}
					go func() {
						for _, v := range origPostValues {
							v.retryCnt++
							// It is difficult to distinguish the error is server error or data error.
							// So, if retryCnt exceeded the configured limit, postValue is considered invalid and abandoned.
							if v.retryCnt > conf.Connection.Post_Metrics_Retry_Max {
								json, err := json.Marshal(v.values)
								if err != nil {
									logger.Errorf("Something wrong with post values. marshaling failed.")
								} else {
									logger.Errorf("Post values may be invalid and abandoned: %s", string(json))
								}
								continue
							}
							postQueue <- v
						}
					}()
					continue
				}
				logger.Debugf("Posting metrics succeeded.")

				if lState == loopStateTerminating && len(postQueue) <= 0 {
					return 0
				}
			}
		}
	}()
}

// collectHostSpecs collects host specs (correspond to "name", "meta" and "interfaces" fields in API v0)
func collectHostSpecs() (string, map[string]interface{}, []map[string]interface{}, error) {
	hostname, err := os.Hostname()
	if err != nil {
		return "", nil, nil, fmt.Errorf("failed to obtain hostname: %s", err.Error())
	}

	meta := spec.Collect(specGenerators())

	interfacesSpec, err := interfaceGenerator().Generate()
	if err != nil {
		return "", nil, nil, fmt.Errorf("failed to collect interfaces: %s", err.Error())
	}

	interfaces, _ := interfacesSpec.([]map[string]interface{})

	return hostname, meta, interfaces, nil
}

// UpdateHostSpecs updates the host information that is already registered on Mackerel.
func UpdateHostSpecs(conf *config.Config, api *mackerel.API, host *mackerel.Host) {
	logger.Debugf("Updating host specs...")

	hostname, meta, interfaces, err := collectHostSpecs()
	if err != nil {
		logger.Errorf("While collecting host specs: %s", err)
		return
	}

	err = api.UpdateHost(host.Id, hostname, meta, interfaces, conf.Roles)
	if err != nil {
		logger.Errorf("Error while updating host specs: %s", err)
	} else {
		logger.Debugf("Host specs sent.")
	}
}

// Prepare sets up API and registers the host data to the Mackerel server.
// Use returned values to call Run().
func Prepare(conf *config.Config) (*mackerel.API, *mackerel.Host, error) {
	api, err := mackerel.NewApi(conf.Apibase, conf.Apikey, conf.Verbose)
	if err != nil {
		return nil, nil, fmt.Errorf("Failed to prepare an api: %s", err.Error())
	}

	host, err := prepareHost(conf.Root, api, conf.Roles)
	if err != nil {
		return nil, nil, fmt.Errorf("Failed to preapre host: %s", err.Error())
	}

	return api, host, nil
}

// Run starts the main metric collecting logic and this function will never return.
func Run(conf *config.Config, api *mackerel.API, host *mackerel.Host, termCh chan struct{}) int {
	logger.Infof("Start: apibase = %s, hostName = %s, hostId = %s", conf.Apibase, host.Name, host.Id)

	ag := &agent.Agent{
		MetricsGenerators: metricsGenerators(conf),
		PluginGenerators:  pluginGenerators(conf),
	}

	delay := delayByHost(host) / 2
	logger.Debugf("wait %d seconds before initial posting.", delay)
	select {
	case <-termCh:
		return 0
	case <-time.After(time.Duration(delay) * time.Second):
		ag.InitPluginGenerators(api)
	}

	return loop(ag, conf, api, host, termCh)
}
<|endoftext|>


<commit_before>// Copyright 2015 Hajime Hoshi
//
// 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.

// +build darwin freebsd
// +build !js
// +build !android

package oto

// #cgo darwin  LDFLAGS: -framework OpenAL
// #cgo freebsd LDFLAGS: -lopenal
//
// #include <stdint.h>
//
// #ifdef __APPLE__
// #include <OpenAL/al.h>
// #include <OpenAL/alc.h>
// #else
// #include <AL/al.h>
// #include <AL/alc.h>
// #endif
//
// static uintptr_t _alcOpenDevice(const ALCchar* name) {
//   return (uintptr_t)alcOpenDevice(name);
// }
//
// static ALCboolean _alcCloseDevice(uintptr_t device) {
//   return alcCloseDevice((void*)device);
// }
//
// static uintptr_t _alcCreateContext(uintptr_t device, const ALCint* attrList) {
//   return (uintptr_t)alcCreateContext((void*)device, attrList);
// }
//
// static ALCenum _alcGetError(uintptr_t device) {
//   return alcGetError((void*)device);
// }
//
// static void _alcMakeContextCurrent(uintptr_t context) {
//   alcMakeContextCurrent((void*)context);
// }
//
// static void _alcDestroyContext(uintptr_t context) {
//   alcDestroyContext((void*)context);
// }
import "C"

import (
	"errors"
	"fmt"
	"runtime"
	"unsafe"
)

// As x/mobile/exp/audio/al is broken on macOS (https://github.com/golang/go/issues/15075),
// and that doesn't support FreeBSD, use OpenAL directly here.

type driver struct {
	// alContext represents a pointer to ALCcontext. The type is uintptr since the value
	// can be 0x18 on macOS, which is invalid as a pointer value, and this might cause
	// GC errors.
	alContext    alContext
	alDevice     alDevice
	alDeviceName string
	alSource     C.ALuint
	sampleRate   int
	isClosed     bool
	alFormat     C.ALenum

	bufs       []C.ALuint
	tmp        []byte
	bufferSize int
}

// alContext is a pointer to OpenAL context.
// The value is not unsafe.Pointer for C.ALCcontext but uintptr,
// because device pointer value can be an invalid value as a pointer on macOS,
// and Cgo pointer checker complains (#65).
type alContext uintptr

// alDevice is a pointer to OpenAL device.
type alDevice uintptr

func (a alDevice) getError() error {
	switch c := C._alcGetError(C.uintptr_t(a)); c {
	case C.ALC_NO_ERROR:
		return nil
	case C.ALC_INVALID_DEVICE:
		return errors.New("OpenAL error: invalid device")
	case C.ALC_INVALID_CONTEXT:
		return errors.New("OpenAL error: invalid context")
	case C.ALC_INVALID_ENUM:
		return errors.New("OpenAL error: invalid enum")
	case C.ALC_INVALID_VALUE:
		return errors.New("OpenAL error: invalid value")
	case C.ALC_OUT_OF_MEMORY:
		return errors.New("OpenAL error: out of memory")
	default:
		return fmt.Errorf("OpenAL error: code %d", c)
	}
}

func alFormat(channelNum, bitDepthInBytes int) C.ALenum {
	switch {
	case channelNum == 1 && bitDepthInBytes == 1:
		return C.AL_FORMAT_MONO8
	case channelNum == 1 && bitDepthInBytes == 2:
		return C.AL_FORMAT_MONO16
	case channelNum == 2 && bitDepthInBytes == 1:
		return C.AL_FORMAT_STEREO8
	case channelNum == 2 && bitDepthInBytes == 2:
		return C.AL_FORMAT_STEREO16
	}
	panic(fmt.Sprintf("oto: invalid channel num (%d) or bytes per sample (%d)", channelNum, bitDepthInBytes))
}

const numBufs = 2

func newDriver(sampleRate, channelNum, bitDepthInBytes, bufferSizeInBytes int) (*driver, error) {
	name := C.alGetString(C.ALC_DEFAULT_DEVICE_SPECIFIER)
	d := alDevice(C._alcOpenDevice((*C.ALCchar)(name)))
	if d == 0 {
		return nil, fmt.Errorf("oto: alcOpenDevice must not return null")
	}
	c := alContext(uintptr(C._alcCreateContext(C.uintptr_t(d), nil)))
	if c == 0 {
		return nil, fmt.Errorf("oto: alcCreateContext must not return null")
	}

	// Don't check getError until making the current context is done.
	// Linux might fail this check even though it succeeds (hajimehoshi/ebiten#204).
	C._alcMakeContextCurrent(C.uintptr_t(c))
	if err := d.getError(); err != nil {
		return nil, fmt.Errorf("oto: Activate: %v", err)
	}

	s := C.ALuint(0)
	C.alGenSources(1, &s)
	if err := d.getError(); err != nil {
		return nil, fmt.Errorf("oto: NewSource: %v", err)
	}

	p := &driver{
		alContext:    c,
		alDevice:     d,
		alSource:     s,
		alDeviceName: C.GoString((*C.char)(name)),
		sampleRate:   sampleRate,
		alFormat:     alFormat(channelNum, bitDepthInBytes),
		bufs:         make([]C.ALuint, numBufs),
		bufferSize:   bufferSizeInBytes,
	}
	runtime.SetFinalizer(p, (*driver).Close)
	C.alGenBuffers(C.ALsizei(numBufs), &p.bufs[0])
	C.alSourcePlay(p.alSource)

	if err := d.getError(); err != nil {
		return nil, fmt.Errorf("oto: Play: %v", err)
	}

	return p, nil
}

func (p *driver) TryWrite(data []byte) (int, error) {
	if err := p.alDevice.getError(); err != nil {
		return 0, fmt.Errorf("oto: starting Write: %v", err)
	}
	n := min(len(data), max(0, p.bufferSize-len(p.tmp)))
	p.tmp = append(p.tmp, data[:n]...)
	if len(p.tmp) < p.bufferSize {
		return n, nil
	}

	pn := C.ALint(0)
	C.alGetSourcei(p.alSource, C.AL_BUFFERS_PROCESSED, &pn)

	if pn > 0 {
		bufs := make([]C.ALuint, pn)
		C.alSourceUnqueueBuffers(p.alSource, C.ALsizei(len(bufs)), &bufs[0])
		if err := p.alDevice.getError(); err != nil {
			return 0, fmt.Errorf("oto: UnqueueBuffers: %v", err)
		}
		p.bufs = append(p.bufs, bufs...)
	}

	if len(p.bufs) == 0 {
		return n, nil
	}

	buf := p.bufs[0]
	p.bufs = p.bufs[1:]
	C.alBufferData(buf, p.alFormat, unsafe.Pointer(&p.tmp[0]), C.ALsizei(p.bufferSize), C.ALsizei(p.sampleRate))
	C.alSourceQueueBuffers(p.alSource, 1, &buf)
	if err := p.alDevice.getError(); err != nil {
		return 0, fmt.Errorf("oto: QueueBuffer: %v", err)
	}

	state := C.ALint(0)
	C.alGetSourcei(p.alSource, C.AL_SOURCE_STATE, &state)
	if state == C.AL_STOPPED || state == C.AL_INITIAL {
		C.alSourceRewind(p.alSource)
		C.alSourcePlay(p.alSource)
		if err := p.alDevice.getError(); err != nil {
			return 0, fmt.Errorf("oto: Rewind or Play: %v", err)
		}
	}

	p.tmp = nil
	return n, nil
}

func (p *driver) Close() error {
	if err := p.alDevice.getError(); err != nil {
		return fmt.Errorf("oto: starting Close: %v", err)
	}
	if p.isClosed {
		return nil
	}

	n := C.ALint(0)
	C.alGetSourcei(p.alSource, C.AL_BUFFERS_QUEUED, &n)
	if 0 < n {
		bs := make([]C.ALuint, n)
		C.alSourceUnqueueBuffers(p.alSource, C.ALsizei(len(bs)), &bs[0])
		p.bufs = append(p.bufs, bs...)
	}

	C.alSourceStop(p.alSource)
	C.alDeleteSources(1, &p.alSource)
	if len(p.bufs) != 0 {
		C.alDeleteBuffers(C.ALsizei(numBufs), &p.bufs[0])
	}
	C._alcDestroyContext(C.uintptr_t(p.alContext))

	if err := p.alDevice.getError(); err != nil {
		return fmt.Errorf("oto: CloseDevice: %v", err)
	}

	b := C._alcCloseDevice(C.uintptr_t(p.alDevice))
	if b == C.ALC_FALSE {
		return fmt.Errorf("oto: CloseDevice: %s failed to close", p.alDeviceName)
	}

	p.isClosed = true
	runtime.SetFinalizer(p, nil)
	return nil
}
<commit_msg>openal: Remove unnecessary type conversion<commit_after>// Copyright 2015 Hajime Hoshi
//
// 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.

// +build darwin freebsd
// +build !js
// +build !android

package oto

// #cgo darwin  LDFLAGS: -framework OpenAL
// #cgo freebsd LDFLAGS: -lopenal
//
// #include <stdint.h>
//
// #ifdef __APPLE__
// #include <OpenAL/al.h>
// #include <OpenAL/alc.h>
// #else
// #include <AL/al.h>
// #include <AL/alc.h>
// #endif
//
// static uintptr_t _alcOpenDevice(const ALCchar* name) {
//   return (uintptr_t)alcOpenDevice(name);
// }
//
// static ALCboolean _alcCloseDevice(uintptr_t device) {
//   return alcCloseDevice((void*)device);
// }
//
// static uintptr_t _alcCreateContext(uintptr_t device, const ALCint* attrList) {
//   return (uintptr_t)alcCreateContext((void*)device, attrList);
// }
//
// static ALCenum _alcGetError(uintptr_t device) {
//   return alcGetError((void*)device);
// }
//
// static void _alcMakeContextCurrent(uintptr_t context) {
//   alcMakeContextCurrent((void*)context);
// }
//
// static void _alcDestroyContext(uintptr_t context) {
//   alcDestroyContext((void*)context);
// }
import "C"

import (
	"errors"
	"fmt"
	"runtime"
	"unsafe"
)

// As x/mobile/exp/audio/al is broken on macOS (https://github.com/golang/go/issues/15075),
// and that doesn't support FreeBSD, use OpenAL directly here.

type driver struct {
	// alContext represents a pointer to ALCcontext. The type is uintptr since the value
	// can be 0x18 on macOS, which is invalid as a pointer value, and this might cause
	// GC errors.
	alContext    alContext
	alDevice     alDevice
	alDeviceName string
	alSource     C.ALuint
	sampleRate   int
	isClosed     bool
	alFormat     C.ALenum

	bufs       []C.ALuint
	tmp        []byte
	bufferSize int
}

// alContext is a pointer to OpenAL context.
// The value is not unsafe.Pointer for C.ALCcontext but uintptr,
// because device pointer value can be an invalid value as a pointer on macOS,
// and Cgo pointer checker complains (#65).
type alContext uintptr

// alDevice is a pointer to OpenAL device.
type alDevice uintptr

func (a alDevice) getError() error {
	switch c := C._alcGetError(C.uintptr_t(a)); c {
	case C.ALC_NO_ERROR:
		return nil
	case C.ALC_INVALID_DEVICE:
		return errors.New("OpenAL error: invalid device")
	case C.ALC_INVALID_CONTEXT:
		return errors.New("OpenAL error: invalid context")
	case C.ALC_INVALID_ENUM:
		return errors.New("OpenAL error: invalid enum")
	case C.ALC_INVALID_VALUE:
		return errors.New("OpenAL error: invalid value")
	case C.ALC_OUT_OF_MEMORY:
		return errors.New("OpenAL error: out of memory")
	default:
		return fmt.Errorf("OpenAL error: code %d", c)
	}
}

func alFormat(channelNum, bitDepthInBytes int) C.ALenum {
	switch {
	case channelNum == 1 && bitDepthInBytes == 1:
		return C.AL_FORMAT_MONO8
	case channelNum == 1 && bitDepthInBytes == 2:
		return C.AL_FORMAT_MONO16
	case channelNum == 2 && bitDepthInBytes == 1:
		return C.AL_FORMAT_STEREO8
	case channelNum == 2 && bitDepthInBytes == 2:
		return C.AL_FORMAT_STEREO16
	}
	panic(fmt.Sprintf("oto: invalid channel num (%d) or bytes per sample (%d)", channelNum, bitDepthInBytes))
}

const numBufs = 2

func newDriver(sampleRate, channelNum, bitDepthInBytes, bufferSizeInBytes int) (*driver, error) {
	name := C.alGetString(C.ALC_DEFAULT_DEVICE_SPECIFIER)
	d := alDevice(C._alcOpenDevice((*C.ALCchar)(name)))
	if d == 0 {
		return nil, fmt.Errorf("oto: alcOpenDevice must not return null")
	}
	c := alContext(C._alcCreateContext(C.uintptr_t(d), nil))
	if c == 0 {
		return nil, fmt.Errorf("oto: alcCreateContext must not return null")
	}

	// Don't check getError until making the current context is done.
	// Linux might fail this check even though it succeeds (hajimehoshi/ebiten#204).
	C._alcMakeContextCurrent(C.uintptr_t(c))
	if err := d.getError(); err != nil {
		return nil, fmt.Errorf("oto: Activate: %v", err)
	}

	s := C.ALuint(0)
	C.alGenSources(1, &s)
	if err := d.getError(); err != nil {
		return nil, fmt.Errorf("oto: NewSource: %v", err)
	}

	p := &driver{
		alContext:    c,
		alDevice:     d,
		alSource:     s,
		alDeviceName: C.GoString((*C.char)(name)),
		sampleRate:   sampleRate,
		alFormat:     alFormat(channelNum, bitDepthInBytes),
		bufs:         make([]C.ALuint, numBufs),
		bufferSize:   bufferSizeInBytes,
	}
	runtime.SetFinalizer(p, (*driver).Close)
	C.alGenBuffers(C.ALsizei(numBufs), &p.bufs[0])
	C.alSourcePlay(p.alSource)

	if err := d.getError(); err != nil {
		return nil, fmt.Errorf("oto: Play: %v", err)
	}

	return p, nil
}

func (p *driver) TryWrite(data []byte) (int, error) {
	if err := p.alDevice.getError(); err != nil {
		return 0, fmt.Errorf("oto: starting Write: %v", err)
	}
	n := min(len(data), max(0, p.bufferSize-len(p.tmp)))
	p.tmp = append(p.tmp, data[:n]...)
	if len(p.tmp) < p.bufferSize {
		return n, nil
	}

	pn := C.ALint(0)
	C.alGetSourcei(p.alSource, C.AL_BUFFERS_PROCESSED, &pn)

	if pn > 0 {
		bufs := make([]C.ALuint, pn)
		C.alSourceUnqueueBuffers(p.alSource, C.ALsizei(len(bufs)), &bufs[0])
		if err := p.alDevice.getError(); err != nil {
			return 0, fmt.Errorf("oto: UnqueueBuffers: %v", err)
		}
		p.bufs = append(p.bufs, bufs...)
	}

	if len(p.bufs) == 0 {
		return n, nil
	}

	buf := p.bufs[0]
	p.bufs = p.bufs[1:]
	C.alBufferData(buf, p.alFormat, unsafe.Pointer(&p.tmp[0]), C.ALsizei(p.bufferSize), C.ALsizei(p.sampleRate))
	C.alSourceQueueBuffers(p.alSource, 1, &buf)
	if err := p.alDevice.getError(); err != nil {
		return 0, fmt.Errorf("oto: QueueBuffer: %v", err)
	}

	state := C.ALint(0)
	C.alGetSourcei(p.alSource, C.AL_SOURCE_STATE, &state)
	if state == C.AL_STOPPED || state == C.AL_INITIAL {
		C.alSourceRewind(p.alSource)
		C.alSourcePlay(p.alSource)
		if err := p.alDevice.getError(); err != nil {
			return 0, fmt.Errorf("oto: Rewind or Play: %v", err)
		}
	}

	p.tmp = nil
	return n, nil
}

func (p *driver) Close() error {
	if err := p.alDevice.getError(); err != nil {
		return fmt.Errorf("oto: starting Close: %v", err)
	}
	if p.isClosed {
		return nil
	}

	n := C.ALint(0)
	C.alGetSourcei(p.alSource, C.AL_BUFFERS_QUEUED, &n)
	if 0 < n {
		bs := make([]C.ALuint, n)
		C.alSourceUnqueueBuffers(p.alSource, C.ALsizei(len(bs)), &bs[0])
		p.bufs = append(p.bufs, bs...)
	}

	C.alSourceStop(p.alSource)
	C.alDeleteSources(1, &p.alSource)
	if len(p.bufs) != 0 {
		C.alDeleteBuffers(C.ALsizei(numBufs), &p.bufs[0])
	}
	C._alcDestroyContext(C.uintptr_t(p.alContext))

	if err := p.alDevice.getError(); err != nil {
		return fmt.Errorf("oto: CloseDevice: %v", err)
	}

	b := C._alcCloseDevice(C.uintptr_t(p.alDevice))
	if b == C.ALC_FALSE {
		return fmt.Errorf("oto: CloseDevice: %s failed to close", p.alDeviceName)
	}

	p.isClosed = true
	runtime.SetFinalizer(p, nil)
	return nil
}
<|endoftext|>


<commit_before>package parsers

import (
	"bufio"
	"os"
	"path"
	"strconv"
	"strings"

	"github.com/AntoineAugusti/moduluschecking/helpers"
	m "github.com/AntoineAugusti/moduluschecking/models"
)

const LINE_NUMBER_SEPARATOR = "/|\\[]"

// A parser that reads data from the filesystem.
type FileParser struct {
	// The path to the weights file.
	weightsPath string
	// The path to the substitutions file for sort codes.
	substitutionsPath string
	// The actual weights for each sort code.
	weights map[string]m.SortCodeData
}

// Get all known sort code substitutions.
func (fp FileParser) Substitutions() map[string]string {
	substitutions := make(map[string]string)

	lines := make(chan string)
	go func() {
		readFile(fp.substitutionsPath, lines, false)
	}()

	for line := range lines {
		fields := strings.Split(line, " ")
		key, value := fields[0], fields[1]
		substitutions[key] = value
	}

	return substitutions
}

// Get the weights, exception information and algorithm to use for all known sort codes.
func (fp FileParser) Weights() map[string]m.SortCodeData {
	jobs := make(chan string)
	results := make(chan m.SortCodeRange)

	go readFile(fp.weightsPath, jobs, true)
	go parseWeightsLine(jobs, results)

	// Process all the sort code ranges
	for result := range results {
		fp.addSortCodeRange(result)
	}

	return fp.weights
}

// Process a sort code range and add it to the weights map.
func (fp *FileParser) addSortCodeRange(scRange m.SortCodeRange) {
	scData := m.SortCodeData{
		Algorithm:      scRange.Algorithm,
		Weights:        scRange.Weights,
		ExceptionValue: scRange.ExceptionValue,
		Next:           nil,
		LineNumber:     scRange.LineNumber,
	}

	// Go over the sort code range and add each sort code in the range in
	// a map to decrease lookup time later. This requires a larger amount of
	// memory, but it seems worth it.
	for sortCode := scRange.Start; sortCode <= scRange.End; sortCode++ {
		key := helpers.AddLeadingZeros(sortCode)
		val, exist := fp.weights[key]
		if !exist {
			fp.weights[key] = scData
			continue
		}

		// Check that the first data structure was before in the weights file
		if val.LineNumber < scData.LineNumber {
			var tmp = val
			tmp.Next = &scData
			fp.weights[key] = tmp
		} else {
			// We read the second weights first.
			// Put it in the right order in the map
			var tmp = val
			scData.Next = &tmp
			fp.weights[key] = scData
		}
	}
}

// Parse lines from the weights file and put the result
// as a SortCodeRange structure in a channel.
func parseWeightsLine(jobs <-chan string, results chan<- m.SortCodeRange) {
	var fields []string

	for line := range jobs {
		tmp := strings.Split(line, LINE_NUMBER_SEPARATOR)
		// Extract the line number in the file because we
		// need to conserve the order of the data
		lineNumber, data := helpers.ToInt(tmp[0]), tmp[1]
		fields = strings.Split(data, ",")
		// Sort code range
		sortCodeStart, sortCodeEnd := helpers.ToInt(fields[0]), helpers.ToInt(fields[1])
		// Algorithm to use in order to perform the check
		algorithm := fields[2]
		// Weights for sort code and account number
		weights := fields[3:17]

		scRange := m.SortCodeRange{
			Start:          sortCodeStart,
			End:            sortCodeEnd,
			Algorithm:      algorithm,
			Weights:        helpers.StringSliceToIntSlice(weights),
			ExceptionValue: 0,
			LineNumber:     lineNumber,
		}

		// Does this sort code range has got an exception?
		hasException := len(fields) > (2 + 1 + 14)

		// Set the exception value if needed
		if hasException {
			scRange.ExceptionValue = helpers.ToInt(fields[17])
		}

		results <- scRange
	}

	close(results)
}

// Read a file and put the content in a channel. We can add the line number
// to each line, if we the order of the file is important. If the boolean is set to true
// each line will be formatted as: <lineNumber>LINE_NUMBER_SEPARATOR<lineData>
func readFile(path string, jobs chan<- string, addLineNumber bool) {
	file, err := os.Open(path)
	if err != nil {
		panic(err)
	}
	defer file.Close()

	lineNumber := 1

	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		txt := scanner.Text()
		if addLineNumber {
			jobs <- strconv.Itoa(lineNumber) + LINE_NUMBER_SEPARATOR + txt
			lineNumber += 1
		} else {
			jobs <- txt
		}
	}

	// We are done with the file, release the channel
	close(jobs)
}

// Create a new instance of a file parser that satisfies
// the parser interface.
func CreateFileParser() m.Parser {
	goPath := path.Clean(os.Getenv("GOPATH"))
	dataPath := goPath + "/src/github.com/AntoineAugusti/moduluschecking/data/"

	return FileParser{
		weightsPath:       dataPath + "weights.txt",
		substitutionsPath: dataPath + "substitutions.txt",
		weights:           make(map[string]m.SortCodeData),
	}
}
<commit_msg>Change the line number separator to be business oriented<commit_after>package parsers

import (
	"bufio"
	"os"
	"path"
	"strconv"
	"strings"

	"github.com/AntoineAugusti/moduluschecking/helpers"
	m "github.com/AntoineAugusti/moduluschecking/models"
)

const LINE_NUMBER_SEPARATOR = "¯\\_(ツ)_/¯"

// A parser that reads data from the filesystem.
type FileParser struct {
	// The path to the weights file.
	weightsPath string
	// The path to the substitutions file for sort codes.
	substitutionsPath string
	// The actual weights for each sort code.
	weights map[string]m.SortCodeData
}

// Get all known sort code substitutions.
func (fp FileParser) Substitutions() map[string]string {
	substitutions := make(map[string]string)

	lines := make(chan string)
	go func() {
		readFile(fp.substitutionsPath, lines, false)
	}()

	for line := range lines {
		fields := strings.Split(line, " ")
		key, value := fields[0], fields[1]
		substitutions[key] = value
	}

	return substitutions
}

// Get the weights, exception information and algorithm to use for all known sort codes.
func (fp FileParser) Weights() map[string]m.SortCodeData {
	jobs := make(chan string)
	results := make(chan m.SortCodeRange)

	go readFile(fp.weightsPath, jobs, true)
	go parseWeightsLine(jobs, results)

	// Process all the sort code ranges
	for result := range results {
		fp.addSortCodeRange(result)
	}

	return fp.weights
}

// Process a sort code range and add it to the weights map.
func (fp *FileParser) addSortCodeRange(scRange m.SortCodeRange) {
	scData := m.SortCodeData{
		Algorithm:      scRange.Algorithm,
		Weights:        scRange.Weights,
		ExceptionValue: scRange.ExceptionValue,
		Next:           nil,
		LineNumber:     scRange.LineNumber,
	}

	// Go over the sort code range and add each sort code in the range in
	// a map to decrease lookup time later. This requires a larger amount of
	// memory, but it seems worth it.
	for sortCode := scRange.Start; sortCode <= scRange.End; sortCode++ {
		key := helpers.AddLeadingZeros(sortCode)
		val, exist := fp.weights[key]
		if !exist {
			fp.weights[key] = scData
			continue
		}

		// Check that the first data structure was before in the weights file
		if val.LineNumber < scData.LineNumber {
			var tmp = val
			tmp.Next = &scData
			fp.weights[key] = tmp
		} else {
			// We read the second weights first.
			// Put it in the right order in the map
			var tmp = val
			scData.Next = &tmp
			fp.weights[key] = scData
		}
	}
}

// Parse lines from the weights file and put the result
// as a SortCodeRange structure in a channel.
func parseWeightsLine(jobs <-chan string, results chan<- m.SortCodeRange) {
	var fields []string

	for line := range jobs {
		tmp := strings.Split(line, LINE_NUMBER_SEPARATOR)
		// Extract the line number in the file because we
		// need to conserve the order of the data
		lineNumber, data := helpers.ToInt(tmp[0]), tmp[1]
		fields = strings.Split(data, ",")
		// Sort code range
		sortCodeStart, sortCodeEnd := helpers.ToInt(fields[0]), helpers.ToInt(fields[1])
		// Algorithm to use in order to perform the check
		algorithm := fields[2]
		// Weights for sort code and account number
		weights := fields[3:17]

		scRange := m.SortCodeRange{
			Start:          sortCodeStart,
			End:            sortCodeEnd,
			Algorithm:      algorithm,
			Weights:        helpers.StringSliceToIntSlice(weights),
			ExceptionValue: 0,
			LineNumber:     lineNumber,
		}

		// Does this sort code range has got an exception?
		hasException := len(fields) > (2 + 1 + 14)

		// Set the exception value if needed
		if hasException {
			scRange.ExceptionValue = helpers.ToInt(fields[17])
		}

		results <- scRange
	}

	close(results)
}

// Read a file and put the content in a channel. We can add the line number
// to each line, if we the order of the file is important. If the boolean is set to true
// each line will be formatted as: <lineNumber>LINE_NUMBER_SEPARATOR<lineData>
func readFile(path string, jobs chan<- string, addLineNumber bool) {
	file, err := os.Open(path)
	if err != nil {
		panic(err)
	}
	defer file.Close()

	lineNumber := 1

	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		txt := scanner.Text()
		if addLineNumber {
			jobs <- strconv.Itoa(lineNumber) + LINE_NUMBER_SEPARATOR + txt
			lineNumber += 1
		} else {
			jobs <- txt
		}
	}

	// We are done with the file, release the channel
	close(jobs)
}

// Create a new instance of a file parser that satisfies
// the parser interface.
func CreateFileParser() m.Parser {
	goPath := path.Clean(os.Getenv("GOPATH"))
	dataPath := goPath + "/src/github.com/AntoineAugusti/moduluschecking/data/"

	return FileParser{
		weightsPath:       dataPath + "weights.txt",
		substitutionsPath: dataPath + "substitutions.txt",
		weights:           make(map[string]m.SortCodeData),
	}
}
<|endoftext|>


<commit_before>package common

import (
	"bytes"
	"strconv"

	. "firempq/api"

	"io"

	"github.com/op/go-logging"
)

// Error translator
var log = logging.MustGetLogger("firempq")

func TranslateError(err error) IResponse {
	if err == nil {
		return OK_RESPONSE
	}
	if resp, ok := err.(IResponse); ok {
		return resp
	} else {
		log.Error(err.Error())
		return ERR_UNKNOWN_ERROR
	}
}

// StrResponse is a simple string response used to return some quick responses for commands like ping, etc.
type StrResponse struct {
	str string
}

func NewStrResponse(str string) *StrResponse {
	return &StrResponse{str: "+" + str}
}

func NewAsyncAccept(str string) *StrResponse {
	return &StrResponse{str: "A " + str}
}

func (r *StrResponse) GetResponse() string {
	return r.str
}

func (r *StrResponse) WriteResponse(buff io.Writer) error {
	_, err := buff.Write(UnsafeStringToBytes(r.GetResponse()))
	return err
}

func (r *StrResponse) IsError() bool {
	return false
}

// IntResponse is a simple integer response.
type IntResponse struct {
	Value int64
}

func NewIntResponse(val int64) *IntResponse {
	return &IntResponse{Value: val}
}

func (r *IntResponse) GetResponse() string {
	return "+DATA :" + strconv.FormatInt(r.Value, 10)
}

var intDataPrefix = []byte("+DATA :")

func (r *IntResponse) WriteResponse(buff io.Writer) error {
	_, err := buff.Write(intDataPrefix)
	if err == nil {
		_, err = buff.Write(UnsafeStringToBytes(strconv.FormatInt(r.Value, 10)))
	}
	return err
}

func (r *IntResponse) IsError() bool {
	return false
}

// StrArrayResponse a response containing array of strings.
type StrArrayResponse struct {
	val []string
}

func NewStrArrayResponse(val []string) *StrArrayResponse {
	return &StrArrayResponse{val: val}
}

func (r *StrArrayResponse) IsError() bool {
	return false
}

func (r *StrArrayResponse) genResponse() []byte {
	var buffer bytes.Buffer
	buffer.WriteString("+DATA *")
	buffer.WriteString(strconv.Itoa(len(r.val)))
	for _, v := range r.val {
		buffer.WriteString("\n")
		buffer.WriteString(v)
	}
	return buffer.Bytes()
}

func (r *StrArrayResponse) GetResponse() string {
	return UnsafeBytesToString(r.genResponse())
}

func (r *StrArrayResponse) WriteResponse(buff io.Writer) error {
	_, err := buff.Write(r.genResponse())
	return err
}

type AsyncResponse struct {
	asyncHeader string
	resp        IResponse
}

func NewAsyncResponse(asyncId string, resp IResponse) IResponse {
	return &AsyncResponse{"+ASYNC " + asyncId + " ", resp}
}

func (r *AsyncResponse) GetResponse() string {
	return r.asyncHeader + r.resp.GetResponse()
}

func (r *AsyncResponse) IsError() bool {
	return r.resp.IsError()
}

func (r *AsyncResponse) WriteResponse(buff io.Writer) error {

	if _, err := buff.Write(UnsafeStringToBytes(r.asyncHeader)); err != nil {
		return err
	}
	return r.resp.WriteResponse(buff)
}

var RESP_PONG IResponse = NewStrResponse("PONG")
var OK_RESPONSE = NewStrResponse("OK")

var _ IResponse = NewStrResponse("test")
var _ IResponse = NewIntResponse(10)
<commit_msg>Async accept response error fix.<commit_after>package common

import (
	"bytes"
	"strconv"

	. "firempq/api"

	"io"

	"github.com/op/go-logging"
)

// Error translator
var log = logging.MustGetLogger("firempq")

func TranslateError(err error) IResponse {
	if err == nil {
		return OK_RESPONSE
	}
	if resp, ok := err.(IResponse); ok {
		return resp
	} else {
		log.Error(err.Error())
		return ERR_UNKNOWN_ERROR
	}
}

// StrResponse is a simple string response used to return some quick responses for commands like ping, etc.
type StrResponse struct {
	str string
}

func NewStrResponse(str string) *StrResponse {
	return &StrResponse{str: "+" + str}
}

func NewAsyncAccept(str string) *StrResponse {
	return &StrResponse{str: "+A " + str}
}

func (r *StrResponse) GetResponse() string {
	return r.str
}

func (r *StrResponse) WriteResponse(buff io.Writer) error {
	_, err := buff.Write(UnsafeStringToBytes(r.GetResponse()))
	return err
}

func (r *StrResponse) IsError() bool {
	return false
}

// IntResponse is a simple integer response.
type IntResponse struct {
	Value int64
}

func NewIntResponse(val int64) *IntResponse {
	return &IntResponse{Value: val}
}

func (r *IntResponse) GetResponse() string {
	return "+DATA :" + strconv.FormatInt(r.Value, 10)
}

var intDataPrefix = []byte("+DATA :")

func (r *IntResponse) WriteResponse(buff io.Writer) error {
	_, err := buff.Write(intDataPrefix)
	if err == nil {
		_, err = buff.Write(UnsafeStringToBytes(strconv.FormatInt(r.Value, 10)))
	}
	return err
}

func (r *IntResponse) IsError() bool {
	return false
}

// StrArrayResponse a response containing array of strings.
type StrArrayResponse struct {
	val []string
}

func NewStrArrayResponse(val []string) *StrArrayResponse {
	return &StrArrayResponse{val: val}
}

func (r *StrArrayResponse) IsError() bool {
	return false
}

func (r *StrArrayResponse) genResponse() []byte {
	var buffer bytes.Buffer
	buffer.WriteString("+DATA *")
	buffer.WriteString(strconv.Itoa(len(r.val)))
	for _, v := range r.val {
		buffer.WriteString("\n")
		buffer.WriteString(v)
	}
	return buffer.Bytes()
}

func (r *StrArrayResponse) GetResponse() string {
	return UnsafeBytesToString(r.genResponse())
}

func (r *StrArrayResponse) WriteResponse(buff io.Writer) error {
	_, err := buff.Write(r.genResponse())
	return err
}

type AsyncResponse struct {
	asyncHeader string
	resp        IResponse
}

func NewAsyncResponse(asyncId string, resp IResponse) IResponse {
	return &AsyncResponse{"+ASYNC " + asyncId + " ", resp}
}

func (r *AsyncResponse) GetResponse() string {
	return r.asyncHeader + r.resp.GetResponse()
}

func (r *AsyncResponse) IsError() bool {
	return r.resp.IsError()
}

func (r *AsyncResponse) WriteResponse(buff io.Writer) error {

	if _, err := buff.Write(UnsafeStringToBytes(r.asyncHeader)); err != nil {
		return err
	}
	return r.resp.WriteResponse(buff)
}

var RESP_PONG IResponse = NewStrResponse("PONG")
var OK_RESPONSE = NewStrResponse("OK")

var _ IResponse = NewStrResponse("test")
var _ IResponse = NewIntResponse(10)
<|endoftext|>


<commit_before>// Copyright 2017 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://gitee.com/johng/gf.

package gconv

import (
    "errors"
    "fmt"
    "gitee.com/johng/gf/g/container/gset"
    "gitee.com/johng/gf/g/util/gstr"
    "gitee.com/johng/gf/third/github.com/fatih/structs"
    "reflect"
    "strings"
)

// 将params键值对参数映射到对应的struct对象属性上，第三个参数mapping为非必需，表示自定义名称与属性名称的映射关系。
// 需要注意：
// 1、第二个参数应当为struct对象指针；
// 2、struct对象的**公开属性(首字母大写)**才能被映射赋值；
// 3、map中的键名可以为小写，映射转换时会自动将键名首字母转为大写做匹配映射，如果无法匹配则忽略；
func Struct(params interface{}, objPointer interface{}, attrMapping...map[string]string) error {
    if params == nil {
        return nil
    }
    isParamMap := true
    paramsMap  := (map[string]interface{})(nil)
    // 先将参数转为 map[string]interface{} 类型
    if m, ok := params.(map[string]interface{}); ok {
        paramsMap = m
    } else {
        paramsMap = make(map[string]interface{})
        if reflect.ValueOf(params).Kind() == reflect.Map {
            ks := reflect.ValueOf(params).MapKeys()
            vs := reflect.ValueOf(params)
            for _, k := range ks {
                paramsMap[String(k.Interface())] = vs.MapIndex(k).Interface()
            }
        } else {
            isParamMap = false
        }
    }
    // struct的反射对象
    elem := reflect.Value{}
    if v, ok := objPointer.(reflect.Value); ok {
        elem = v
    } else {
        elem = reflect.ValueOf(objPointer).Elem()
    }
    // 如果给定的参数不是map类型，那么直接将参数值映射到第一个属性上
    if !isParamMap {
        if err := bindVarToStructByIndex(elem, 0, params); err != nil {
            return err
        }
        return nil
    }
    // 已执行过转换的属性，只执行一次转换
    dmap := make(map[string]bool)
    // 首先按照传递的映射关系进行匹配
    if len(attrMapping) > 0 && len(attrMapping[0]) > 0 {
        for mappingk, mappingv := range attrMapping[0] {
            if v, ok := paramsMap[mappingk]; ok {
                dmap[mappingv] = true
                if err := bindVarToStructAttr(elem, mappingv, v); err != nil {
                    return err
                }
            }
        }
    }
    // 其次匹配对象定义时绑定的属性名称
    // 标签映射关系map，如果有的话
    tagmap := getTagMapOfStruct(objPointer)
    for tagk, tagv := range tagmap {
        if _, ok := dmap[tagv]; ok {
            continue
        }
        if v, ok := paramsMap[tagk]; ok {
            dmap[tagv] = true
            if err := bindVarToStructAttr(elem, tagv, v); err != nil {
                return err
            }
        }
    }
    // 最后按照默认规则进行匹配
    attrset  := gset.NewStringSet(true)
    elemtype := elem.Type()
    for i := 0; i < elem.NumField(); i++ {
        attrset.Add(elemtype.Field(i).Name)
    }
    for mapk, mapv := range paramsMap {
        name := ""
        for _, checkName := range []string {
            gstr.UcFirst(mapk),
            gstr.ReplaceByMap(mapk, map[string]string{
                "_" : "",
                "-" : "",
                " " : "",
            })} {
            if _, ok := dmap[checkName]; ok {
                continue
            }
            if _, ok := tagmap[checkName]; ok {
                continue
            }
            // 循环查找属性名称进行匹配
            attrset.Iterator(func(value string) bool {
                if strings.EqualFold(checkName, value) {
                    name = value
                    return false
                }
                if strings.EqualFold(checkName, gstr.Replace(value, "_", "")) {
                    name = value
                    return false
                }
                return true
            })
            if name != "" {
                break
            }
        }
        // 如果没有匹配到属性名称，放弃
        if name == "" {
            continue
        }
        if err := bindVarToStructAttr(elem, name, mapv); err != nil {
            return err
        }
    }
    return nil
}

// 解析指针对象的tag
func getTagMapOfStruct(objPointer interface{}) map[string]string {
    tagmap := make(map[string]string)
    // 反射类型判断
    fields := ([]*structs.Field)(nil)
    if v, ok := objPointer.(reflect.Value); ok {
        fields = structs.Fields(v.Interface())
    } else {
        fields = structs.Fields(objPointer)
    }
    // 将struct中定义的属性转换名称构建成tagmap
    for _, field := range fields {
        if tag := field.Tag("gconv"); tag != "" {
            for _, v := range strings.Split(tag, ",") {
                tagmap[strings.TrimSpace(v)] = field.Name()
            }
        }
    }
    return tagmap
}

// 将参数值绑定到对象指定名称的属性上
func bindVarToStructAttr(elem reflect.Value, name string, value interface{}) (err error) {
    structFieldValue := elem.FieldByName(name)
    // 键名与对象属性匹配检测，map中如果有struct不存在的属性，那么不做处理，直接return
    if !structFieldValue.IsValid() {
        return nil
    }
    // CanSet的属性必须为公开属性(首字母大写)
    if !structFieldValue.CanSet() {
        return nil
    }
    // 必须将value转换为struct属性的数据类型，这里必须用到gconv包
    defer func() {
        // 如果转换失败，那么可能是类型不匹配造成(例如属性包含自定义类型)，那么执行递归转换
        if recover() != nil {
            err = bindVarToReflectValue(structFieldValue, value)
        }
    }()
    structFieldValue.Set(reflect.ValueOf(Convert(value, structFieldValue.Type().String())))
    return nil
}

// 将参数值绑定到对象指定索引位置的属性上
func bindVarToStructByIndex(elem reflect.Value, index int, value interface{}) (err error) {
    structFieldValue := elem.FieldByIndex([]int{index})
    // 键名与对象属性匹配检测
    if !structFieldValue.IsValid() {
        return nil
    }
    // CanSet的属性必须为公开属性(首字母大写)
    if !structFieldValue.CanSet() {
        return nil
    }
    // 必须将value转换为struct属性的数据类型，这里必须用到gconv包
    defer func() {
        // 如果转换失败，那么可能是类型不匹配造成(例如属性包含自定义类型)，那么执行递归转换
        if recover() != nil {
            err = bindVarToReflectValue(structFieldValue, value)
        }
    }()
    structFieldValue.Set(reflect.ValueOf(Convert(value, structFieldValue.Type().String())))
    return nil
}

// 当默认的基本类型转换失败时，通过recover判断后执行反射类型转换(处理复杂类型)
func bindVarToReflectValue(structFieldValue reflect.Value, value interface{}) error {
    switch structFieldValue.Kind() {
        // 属性为结构体
        case reflect.Struct:
            Struct(value, structFieldValue)

        // 属性为数组类型
        case reflect.Slice: fallthrough
        case reflect.Array:
            a := reflect.Value{}
            v := reflect.ValueOf(value)
            if v.Kind() == reflect.Slice || v.Kind() == reflect.Array {
                if v.Len() > 0 {
                    a  = reflect.MakeSlice(structFieldValue.Type(), v.Len(), v.Len())
                    t := a.Index(0).Type()
                    for i := 0; i < v.Len(); i++ {
                        if t.Kind() == reflect.Ptr {
                            e := reflect.New(t.Elem()).Elem()
                            Struct(v.Index(i).Interface(), e)
                            a.Index(i).Set(e.Addr())
                        } else {
                            e := reflect.New(t).Elem()
                            Struct(v.Index(i).Interface(), e)
                            a.Index(i).Set(e)
                        }
                    }
                }
            } else {
                a  = reflect.MakeSlice(structFieldValue.Type(), 1, 1)
                t := a.Index(0).Type()
                if t.Kind() == reflect.Ptr {
                    e := reflect.New(t.Elem()).Elem()
                    Struct(value, e)
                    a.Index(0).Set(e.Addr())
                } else {
                    e := reflect.New(t).Elem()
                    Struct(value, e)
                    a.Index(0).Set(e)
                }
            }
            structFieldValue.Set(a)

        // 属性为指针类型
        case reflect.Ptr:
            e := reflect.New(structFieldValue.Type().Elem()).Elem()
            Struct(value, e)
            structFieldValue.Set(e.Addr())

        default:
            return errors.New(fmt.Sprintf(`cannot convert to type "%s"`, structFieldValue.Type().String()))
    }
    return nil
}

<commit_msg>add json tag support for struct converting of gconv<commit_after>// Copyright 2017 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://gitee.com/johng/gf.

package gconv

import (
    "errors"
    "fmt"
    "gitee.com/johng/gf/g/container/gset"
    "gitee.com/johng/gf/g/util/gstr"
    "gitee.com/johng/gf/third/github.com/fatih/structs"
    "reflect"
    "strings"
)

// 将params键值对参数映射到对应的struct对象属性上，第三个参数mapping为非必需，表示自定义名称与属性名称的映射关系。
// 需要注意：
// 1、第二个参数应当为struct对象指针；
// 2、struct对象的**公开属性(首字母大写)**才能被映射赋值；
// 3、map中的键名可以为小写，映射转换时会自动将键名首字母转为大写做匹配映射，如果无法匹配则忽略；
func Struct(params interface{}, objPointer interface{}, attrMapping...map[string]string) error {
    if params == nil {
        return nil
    }
    isParamMap := true
    paramsMap  := (map[string]interface{})(nil)
    // 先将参数转为 map[string]interface{} 类型
    if m, ok := params.(map[string]interface{}); ok {
        paramsMap = m
    } else {
        paramsMap = make(map[string]interface{})
        if reflect.ValueOf(params).Kind() == reflect.Map {
            ks := reflect.ValueOf(params).MapKeys()
            vs := reflect.ValueOf(params)
            for _, k := range ks {
                paramsMap[String(k.Interface())] = vs.MapIndex(k).Interface()
            }
        } else {
            isParamMap = false
        }
    }
    // struct的反射对象
    elem := reflect.Value{}
    if v, ok := objPointer.(reflect.Value); ok {
        elem = v
    } else {
        elem = reflect.ValueOf(objPointer).Elem()
    }
    // 如果给定的参数不是map类型，那么直接将参数值映射到第一个属性上
    if !isParamMap {
        if err := bindVarToStructByIndex(elem, 0, params); err != nil {
            return err
        }
        return nil
    }
    // 已执行过转换的属性，只执行一次转换
    dmap := make(map[string]bool)
    // 首先按照传递的映射关系进行匹配
    if len(attrMapping) > 0 && len(attrMapping[0]) > 0 {
        for mappingk, mappingv := range attrMapping[0] {
            if v, ok := paramsMap[mappingk]; ok {
                dmap[mappingv] = true
                if err := bindVarToStructAttr(elem, mappingv, v); err != nil {
                    return err
                }
            }
        }
    }
    // 其次匹配对象定义时绑定的属性名称
    // 标签映射关系map，如果有的话
    tagmap := getTagMapOfStruct(objPointer)
    for tagk, tagv := range tagmap {
        if _, ok := dmap[tagv]; ok {
            continue
        }
        if v, ok := paramsMap[tagk]; ok {
            dmap[tagv] = true
            if err := bindVarToStructAttr(elem, tagv, v); err != nil {
                return err
            }
        }
    }
    // 最后按照默认规则进行匹配
    attrset  := gset.NewStringSet(true)
    elemtype := elem.Type()
    for i := 0; i < elem.NumField(); i++ {
        attrset.Add(elemtype.Field(i).Name)
    }
    for mapk, mapv := range paramsMap {
        name := ""
        for _, checkName := range []string {
            gstr.UcFirst(mapk),
            gstr.ReplaceByMap(mapk, map[string]string{
                "_" : "",
                "-" : "",
                " " : "",
            })} {
            if _, ok := dmap[checkName]; ok {
                continue
            }
            if _, ok := tagmap[checkName]; ok {
                continue
            }
            // 循环查找属性名称进行匹配
            attrset.Iterator(func(value string) bool {
                if strings.EqualFold(checkName, value) {
                    name = value
                    return false
                }
                if strings.EqualFold(checkName, gstr.Replace(value, "_", "")) {
                    name = value
                    return false
                }
                return true
            })
            if name != "" {
                break
            }
        }
        // 如果没有匹配到属性名称，放弃
        if name == "" {
            continue
        }
        if err := bindVarToStructAttr(elem, name, mapv); err != nil {
            return err
        }
    }
    return nil
}

// 解析指针对象的tag
func getTagMapOfStruct(objPointer interface{}) map[string]string {
    tagmap := make(map[string]string)
    // 反射类型判断
    fields := ([]*structs.Field)(nil)
    if v, ok := objPointer.(reflect.Value); ok {
        fields = structs.Fields(v.Interface())
    } else {
        fields = structs.Fields(objPointer)
    }
    // 将struct中定义的属性转换名称构建成tagmap
    for _, field := range fields {
        tag := field.Tag("gconv")
        if tag == "" {
            tag = field.Tag("json")
        }
        if tag != "" {
            for _, v := range strings.Split(tag, ",") {
                tagmap[strings.TrimSpace(v)] = field.Name()
            }
        }
    }
    return tagmap
}

// 将参数值绑定到对象指定名称的属性上
func bindVarToStructAttr(elem reflect.Value, name string, value interface{}) (err error) {
    structFieldValue := elem.FieldByName(name)
    // 键名与对象属性匹配检测，map中如果有struct不存在的属性，那么不做处理，直接return
    if !structFieldValue.IsValid() {
        return nil
    }
    // CanSet的属性必须为公开属性(首字母大写)
    if !structFieldValue.CanSet() {
        return nil
    }
    // 必须将value转换为struct属性的数据类型，这里必须用到gconv包
    defer func() {
        // 如果转换失败，那么可能是类型不匹配造成(例如属性包含自定义类型)，那么执行递归转换
        if recover() != nil {
            err = bindVarToReflectValue(structFieldValue, value)
        }
    }()
    structFieldValue.Set(reflect.ValueOf(Convert(value, structFieldValue.Type().String())))
    return nil
}

// 将参数值绑定到对象指定索引位置的属性上
func bindVarToStructByIndex(elem reflect.Value, index int, value interface{}) (err error) {
    structFieldValue := elem.FieldByIndex([]int{index})
    // 键名与对象属性匹配检测
    if !structFieldValue.IsValid() {
        return nil
    }
    // CanSet的属性必须为公开属性(首字母大写)
    if !structFieldValue.CanSet() {
        return nil
    }
    // 必须将value转换为struct属性的数据类型，这里必须用到gconv包
    defer func() {
        // 如果转换失败，那么可能是类型不匹配造成(例如属性包含自定义类型)，那么执行递归转换
        if recover() != nil {
            err = bindVarToReflectValue(structFieldValue, value)
        }
    }()
    structFieldValue.Set(reflect.ValueOf(Convert(value, structFieldValue.Type().String())))
    return nil
}

// 当默认的基本类型转换失败时，通过recover判断后执行反射类型转换(处理复杂类型)
func bindVarToReflectValue(structFieldValue reflect.Value, value interface{}) error {
    switch structFieldValue.Kind() {
        // 属性为结构体
        case reflect.Struct:
            Struct(value, structFieldValue)

        // 属性为数组类型
        case reflect.Slice: fallthrough
        case reflect.Array:
            a := reflect.Value{}
            v := reflect.ValueOf(value)
            if v.Kind() == reflect.Slice || v.Kind() == reflect.Array {
                if v.Len() > 0 {
                    a  = reflect.MakeSlice(structFieldValue.Type(), v.Len(), v.Len())
                    t := a.Index(0).Type()
                    for i := 0; i < v.Len(); i++ {
                        if t.Kind() == reflect.Ptr {
                            e := reflect.New(t.Elem()).Elem()
                            Struct(v.Index(i).Interface(), e)
                            a.Index(i).Set(e.Addr())
                        } else {
                            e := reflect.New(t).Elem()
                            Struct(v.Index(i).Interface(), e)
                            a.Index(i).Set(e)
                        }
                    }
                }
            } else {
                a  = reflect.MakeSlice(structFieldValue.Type(), 1, 1)
                t := a.Index(0).Type()
                if t.Kind() == reflect.Ptr {
                    e := reflect.New(t.Elem()).Elem()
                    Struct(value, e)
                    a.Index(0).Set(e.Addr())
                } else {
                    e := reflect.New(t).Elem()
                    Struct(value, e)
                    a.Index(0).Set(e)
                }
            }
            structFieldValue.Set(a)

        // 属性为指针类型
        case reflect.Ptr:
            e := reflect.New(structFieldValue.Type().Elem()).Elem()
            Struct(value, e)
            structFieldValue.Set(e.Addr())

        default:
            return errors.New(fmt.Sprintf(`cannot convert to type "%s"`, structFieldValue.Type().String()))
    }
    return nil
}

<|endoftext|>


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

// A small helper for using gcsfuse with mount(8).
//
// Can be invoked using a command-line of the form expected for mount helpers.
// Calls the gcsfuse binary, which must be in $PATH, and waits for it to
// complete. The device is passed as --bucket, and other known options are
// converted to appropriate flags.
//
// This binary does not daemonize, and therefore must be used with a wrapper
// that performs daemonization if it is to be used directly with mount(8).
package main

import (
	"fmt"
	"log"
	"os"
	"os/exec"
	"strings"
)

// A 'name=value' mount option. If '=value' is not present, only the name will
// be filled in.
type Option struct {
	Name  string
	Value string
}

// Parse a single comma-separated list of mount options.
func parseOpts(s string) (opts []Option, err error) {
	// NOTE(jacobsa): The man pages don't define how escaping works, and as far
	// as I can tell there is no way to properly escape or quote a comma in the
	// options list for an fstab entry. So put our fingers in our ears and hope
	// that nobody needs a comma.
	for _, p := range strings.Split(s, ",") {
		var opt Option

		// Split on the first equals sign.
		if equalsIndex := strings.IndexByte(p, '='); equalsIndex != -1 {
			opt.Name = p[:equalsIndex]
			opt.Value = p[equalsIndex+1:]
		} else {
			opt.Name = p
		}

		opts = append(opts, opt)
	}

	return
}

// Attempt to parse the terrible undocumented format that mount(8) gives us.
// Return the 'device' (aka 'special' on OS X), the mount point, and a list of
// mount options encountered.
func parseArgs() (device string, mountPoint string, opts []Option, err error) {
	// Example invocation on OS X:
	//
	//     mount -t porp -o key_file=/some\ file.json -o ro,blah bucket ~/tmp/mp
	//
	// becomes the following arguments:
	//
	//     Arg 0: "/path/to/gcsfuse_mount_helper"
	//     Arg 1: "-o"
	//     Arg 2: "key_file=/some file.json"
	//     Arg 3: "-o"
	//     Arg 4: "ro"
	//     Arg 5: "-o"
	//     Arg 6: "blah"
	//     Arg 7: "bucket"
	//     Arg 8: "/path/to/mp"
	//
	// On Linux, the fstab entry
	//
	//     bucket /path/to/mp porp user,key_file=/some\040file.json
	//
	// becomes
	//
	//     Arg 0: "/path/to/gcsfuse_mount_helper"
	//     Arg 1: "bucket"
	//     Arg 2: "/path/to/mp"
	//     Arg 3: "-o"
	//     Arg 4: "rw,noexec,nosuid,nodev,user,key_file=/some file.json"
	//

	// Linux and OS X differ on the position of the options. So scan all
	// arguments (aside from the name of the binary), and:
	//
	//  *  Treat the first argument not following "-o" as the device name.
	//  *  Treat the second argument not following "-o" as the mount point.
	//  *  Treat the third argument not following "-o" as an error.
	//  *  Treat all arguments following "-o" as comma-separated options lists.
	//
	rawArgs := 0
	for i, arg := range os.Args {
		// Skip the binary name.
		if i == 0 {
			continue
		}

		// Skip "-o"; we will look back on the next iteration.
		if arg == "-o" {
			continue
		}

		// If the previous argument was "-o", this is a list of options.
		if os.Args[i-1] == "-o" {
			var tmp []Option
			tmp, err = parseOpts(arg)
			if err != nil {
				err = fmt.Errorf("parseOpts(%q): %v", arg, err)
				return
			}

			opts = append(opts, tmp...)
			continue
		}

		// Otherwise this is a non-option argument.
		switch rawArgs {
		case 0:
			device = arg

		case 1:
			mountPoint = arg

		default:
			err = fmt.Errorf(
				"Too many non-option arguments. The straw that broke the "+
					"camel's back: %q",
				arg)
			return
		}

		rawArgs++
	}

	// Did we see all of the raw arguments we expected?
	if rawArgs != 2 {
		err = fmt.Errorf("Expected 2 non-option arguments; got %d", rawArgs)
	}

	return
}

// Turn mount-style options into gcsfuse arguments. Skip known detritus that
// the mount command gives us.
//
// The result of this function should be appended to exec.Command.Args.
func makeGcsfuseArgs(
	device string,
	mountPoint string,
	opts []Option) (args []string, err error) {
	// Deal with options.
	for _, opt := range opts {
		switch opt.Name {
		case "key_file":
			args = append(args, "--key_file="+opt.Value)

		case "fuse_debug":
			args = append(args, "--fuse.debug")

		case "gcs_debug":
			args = append(args, "--gcs.debug")

		case "ro":
			args = append(args, "--read_only")

		// Ignore arguments for default and unsupported behavior automatically
		// added by mount(8) on Linux.
		case "rw":
		case "noexec":
		case "nosuid":
		case "nodev":
		case "user":

		default:
			err = fmt.Errorf(
				"Unrecognized mount option: %q (value %q)",
				opt.Name,
				opt.Value)

			return
		}
	}

	// Set the bucket.
	args = append(args, "--bucket="+device)

	// Set the mount point.
	args = append(args, "--mount_point="+mountPoint)

	return
}

func main() {
	// Print out each argument.
	for i, arg := range os.Args {
		log.Printf("Arg %d: %q", i, arg)
	}

	// Attempt to parse arguments.
	device, mountPoint, opts, err := parseArgs()
	if err != nil {
		log.Fatalf("parseArgs: %v", err)
	}

	// Print what we gleaned.
	log.Printf("Device: %q", device)
	log.Printf("Mount point: %q", mountPoint)
	for _, opt := range opts {
		log.Printf("Option %q: %q", opt.Name, opt.Value)
	}

	// Choose gcsfuse args.
	gcsfuseArgs, err := makeGcsfuseArgs(device, mountPoint, opts)
	if err != nil {
		log.Fatalf("makeGcsfuseArgs: %v", err)
	}

	for _, a := range gcsfuseArgs {
		log.Printf("gcsfuse arg: %q", a)
	}

	// Run gcsfuse and wait for it to complete.
	cmd := exec.Command("gcsfuse", gcsfuseArgs...)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	err = cmd.Run()
	if err != nil {
		log.Fatalf("gcsfuse failed or failed to run: %v", err)
	}

	log.Println("gcsfuse completed successfully.")
}
<commit_msg>Began on a slice flag.<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// A small helper for using gcsfuse with mount(8).
//
// Can be invoked using a command-line of the form expected for mount helpers.
// Calls the gcsfuse binary, which must be in $PATH, and waits for it to
// complete. The device is passed as --bucket, and other known options are
// converted to appropriate flags.
//
// This binary does not daemonize, and therefore must be used with a wrapper
// that performs daemonization if it is to be used directly with mount(8).
package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"os/exec"
	"strings"
)

var fOptions OptionSlice

func init() {
	flag.Var(&fOptions, "o", "Mount options. May be repeated.")
}

// A 'name=value' mount option. If '=value' is not present, only the name will
// be filled in.
type Option struct {
	Name  string
	Value string
}

// A slice of options that knows how to parse command-line flags into the
// slice, implementing flag.Value.
type OptionSlice []Option

// Parse a single comma-separated list of mount options.
func parseOpts(s string) (opts []Option, err error) {
	// NOTE(jacobsa): The man pages don't define how escaping works, and as far
	// as I can tell there is no way to properly escape or quote a comma in the
	// options list for an fstab entry. So put our fingers in our ears and hope
	// that nobody needs a comma.
	for _, p := range strings.Split(s, ",") {
		var opt Option

		// Split on the first equals sign.
		if equalsIndex := strings.IndexByte(p, '='); equalsIndex != -1 {
			opt.Name = p[:equalsIndex]
			opt.Value = p[equalsIndex+1:]
		} else {
			opt.Name = p
		}

		opts = append(opts, opt)
	}

	return
}

// Attempt to parse the terrible undocumented format that mount(8) gives us.
// Return the 'device' (aka 'special' on OS X), the mount point, and a list of
// mount options encountered.
func parseArgs() (device string, mountPoint string, opts []Option, err error) {
	// Example invocation on OS X:
	//
	//     mount -t porp -o key_file=/some\ file.json -o ro,blah bucket ~/tmp/mp
	//
	// becomes the following arguments:
	//
	//     Arg 0: "/path/to/gcsfuse_mount_helper"
	//     Arg 1: "-o"
	//     Arg 2: "key_file=/some file.json"
	//     Arg 3: "-o"
	//     Arg 4: "ro"
	//     Arg 5: "-o"
	//     Arg 6: "blah"
	//     Arg 7: "bucket"
	//     Arg 8: "/path/to/mp"
	//
	// On Linux, the fstab entry
	//
	//     bucket /path/to/mp porp user,key_file=/some\040file.json
	//
	// becomes
	//
	//     Arg 0: "/path/to/gcsfuse_mount_helper"
	//     Arg 1: "bucket"
	//     Arg 2: "/path/to/mp"
	//     Arg 3: "-o"
	//     Arg 4: "rw,noexec,nosuid,nodev,user,key_file=/some file.json"
	//

	// Linux and OS X differ on the position of the options. So scan all
	// arguments (aside from the name of the binary), and:
	//
	//  *  Treat the first argument not following "-o" as the device name.
	//  *  Treat the second argument not following "-o" as the mount point.
	//  *  Treat the third argument not following "-o" as an error.
	//  *  Treat all arguments following "-o" as comma-separated options lists.
	//
	rawArgs := 0
	for i, arg := range os.Args {
		// Skip the binary name.
		if i == 0 {
			continue
		}

		// Skip "-o"; we will look back on the next iteration.
		if arg == "-o" {
			continue
		}

		// If the previous argument was "-o", this is a list of options.
		if os.Args[i-1] == "-o" {
			var tmp []Option
			tmp, err = parseOpts(arg)
			if err != nil {
				err = fmt.Errorf("parseOpts(%q): %v", arg, err)
				return
			}

			opts = append(opts, tmp...)
			continue
		}

		// Otherwise this is a non-option argument.
		switch rawArgs {
		case 0:
			device = arg

		case 1:
			mountPoint = arg

		default:
			err = fmt.Errorf(
				"Too many non-option arguments. The straw that broke the "+
					"camel's back: %q",
				arg)
			return
		}

		rawArgs++
	}

	// Did we see all of the raw arguments we expected?
	if rawArgs != 2 {
		err = fmt.Errorf("Expected 2 non-option arguments; got %d", rawArgs)
	}

	return
}

// Turn mount-style options into gcsfuse arguments. Skip known detritus that
// the mount command gives us.
//
// The result of this function should be appended to exec.Command.Args.
func makeGcsfuseArgs(
	device string,
	mountPoint string,
	opts []Option) (args []string, err error) {
	// Deal with options.
	for _, opt := range opts {
		switch opt.Name {
		case "key_file":
			args = append(args, "--key_file="+opt.Value)

		case "fuse_debug":
			args = append(args, "--fuse.debug")

		case "gcs_debug":
			args = append(args, "--gcs.debug")

		case "ro":
			args = append(args, "--read_only")

		// Ignore arguments for default and unsupported behavior automatically
		// added by mount(8) on Linux.
		case "rw":
		case "noexec":
		case "nosuid":
		case "nodev":
		case "user":

		default:
			err = fmt.Errorf(
				"Unrecognized mount option: %q (value %q)",
				opt.Name,
				opt.Value)

			return
		}
	}

	// Set the bucket.
	args = append(args, "--bucket="+device)

	// Set the mount point.
	args = append(args, "--mount_point="+mountPoint)

	return
}

func main() {
	// Print out each argument.
	for i, arg := range os.Args {
		log.Printf("Arg %d: %q", i, arg)
	}

	// Attempt to parse arguments.
	device, mountPoint, opts, err := parseArgs()
	if err != nil {
		log.Fatalf("parseArgs: %v", err)
	}

	// Print what we gleaned.
	log.Printf("Device: %q", device)
	log.Printf("Mount point: %q", mountPoint)
	for _, opt := range opts {
		log.Printf("Option %q: %q", opt.Name, opt.Value)
	}

	// Choose gcsfuse args.
	gcsfuseArgs, err := makeGcsfuseArgs(device, mountPoint, opts)
	if err != nil {
		log.Fatalf("makeGcsfuseArgs: %v", err)
	}

	for _, a := range gcsfuseArgs {
		log.Printf("gcsfuse arg: %q", a)
	}

	// Run gcsfuse and wait for it to complete.
	cmd := exec.Command("gcsfuse", gcsfuseArgs...)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	err = cmd.Run()
	if err != nil {
		log.Fatalf("gcsfuse failed or failed to run: %v", err)
	}

	log.Println("gcsfuse completed successfully.")
}
<|endoftext|>


<commit_before>package controllers

import (
	"github.com/Sirupsen/logrus"
	"github.com/etcinit/gonduit/constants"
	"github.com/etcinit/phabulous/app/bot"
	"github.com/etcinit/phabulous/app/factories"
	"github.com/etcinit/phabulous/app/messages"
	"github.com/etcinit/phabulous/app/resolvers"
	"github.com/gin-gonic/gin"
	"github.com/jacobstr/confer"
)

// FeedController handles feed webhook routes
type FeedController struct {
	Config       *confer.Config                  `inject:""`
	Slacker      *bot.SlackService               `inject:""`
	Factory      *factories.GonduitFactory       `inject:""`
	Commits      *resolvers.CommitResolver       `inject:""`
	Tasks        *resolvers.TaskResolver         `inject:""`
	Differential *resolvers.DifferentialResolver `inject:""`
	Logger       *logrus.Logger                  `inject:""`
}

// Register registers the route handlers for this controller
func (f *FeedController) Register(r *gin.RouterGroup) {
	front := r.Group("/feed")
	{
		front.POST("/receive", f.postReceive)
	}
}

func (f *FeedController) postReceive(c *gin.Context) {
	conduit, err := f.Factory.Make()

	if err != nil {
		panic(err)
	}

	c.Request.ParseForm()

	res, err := conduit.PHIDQuerySingle(
		string(c.Request.PostForm.Get("storyData[objectPHID]")),
	)

	if err != nil {
		panic(err)
	}

	storyText := c.Request.PostForm.Get("storyText")

	if res.URI != "" {
		storyText += " (<" + res.URI + "|More info>)"
	}

	phidType := constants.PhidType(res.Type)
	icon := messages.PhidTypeToIcon(phidType)

	f.Slacker.FeedPost(storyText)

	switch phidType {
	case constants.PhidTypeCommit:
		channelName, err := f.Commits.Resolve(res.Name)
		if err != nil {
			f.Logger.Error(err)
		}

		if channelName != "" {
			f.Slacker.SimplePost(channelName, storyText, icon, false)
		}
		break
	case constants.PhidTypeTask:
		channelName, err := f.Tasks.Resolve(res.PHID)
		if err != nil {
			f.Logger.Error(err)
		}

		if channelName != "" {
			f.Slacker.SimplePost(channelName, storyText, icon, false)
		}
		break
	case constants.PhidTypeDifferentialRevision:
		channelName, err := f.Differential.Resolve(res.PHID)
		if err != nil {
			f.Logger.Error(err)
		}

		if channelName != "" {
			f.Slacker.SimplePost(channelName, storyText, icon, false)
		}
		break
	}

	c.JSON(200, gin.H{
		"status": "success",
		"messages": []string{
			"OK",
		},
	})
}
<commit_msg>Add experimental support for "all" channel (#11)<commit_after>package controllers

import (
	"github.com/Sirupsen/logrus"
	"github.com/etcinit/gonduit/constants"
	"github.com/etcinit/phabulous/app/bot"
	"github.com/etcinit/phabulous/app/factories"
	"github.com/etcinit/phabulous/app/messages"
	"github.com/etcinit/phabulous/app/resolvers"
	"github.com/gin-gonic/gin"
	"github.com/jacobstr/confer"
)

// FeedController handles feed webhook routes
type FeedController struct {
	Config       *confer.Config                  `inject:""`
	Slacker      *bot.SlackService               `inject:""`
	Factory      *factories.GonduitFactory       `inject:""`
	Commits      *resolvers.CommitResolver       `inject:""`
	Tasks        *resolvers.TaskResolver         `inject:""`
	Differential *resolvers.DifferentialResolver `inject:""`
	Logger       *logrus.Logger                  `inject:""`
}

// Register registers the route handlers for this controller
func (f *FeedController) Register(r *gin.RouterGroup) {
	front := r.Group("/feed")
	{
		front.POST("/receive", f.postReceive)
	}
}

func (f *FeedController) postReceive(c *gin.Context) {
	conduit, err := f.Factory.Make()

	if err != nil {
		panic(err)
	}

	c.Request.ParseForm()

	res, err := conduit.PHIDQuerySingle(
		string(c.Request.PostForm.Get("storyData[objectPHID]")),
	)

	if err != nil {
		panic(err)
	}

	storyText := c.Request.PostForm.Get("storyText")

	if res.URI != "" {
		storyText += " (<" + res.URI + "|More info>)"
	}

	phidType := constants.PhidType(res.Type)
	icon := messages.PhidTypeToIcon(phidType)

	f.Slacker.FeedPost(storyText)

	switch phidType {
	case constants.PhidTypeCommit:
		channelName, err := f.Commits.Resolve(res.Name)
		if err != nil {
			f.Logger.Error(err)
		}

		if channelName != "" {
			f.Slacker.SimplePost(channelName, storyText, icon, false)
		}

		// Support "all" channel.
		channelMap := f.Config.GetStringMapString("channels.repositories")

		if channelName, ok := channelMap["all"]; ok == true {
			f.Slacker.SimplePost(channelName, storyText, icon, false)
		}
		break
	case constants.PhidTypeTask:
		channelName, err := f.Tasks.Resolve(res.PHID)
		if err != nil {
			f.Logger.Error(err)
		}

		if channelName != "" {
			f.Slacker.SimplePost(channelName, storyText, icon, false)
		}
		break
	case constants.PhidTypeDifferentialRevision:
		channelName, err := f.Differential.Resolve(res.PHID)
		if err != nil {
			f.Logger.Error(err)
		}

		if channelName != "" {
			f.Slacker.SimplePost(channelName, storyText, icon, false)
		}

		// Support "all" channel.
		channelMap := f.Config.GetStringMapString("channels.repositories")

		if channelName, ok := channelMap["all"]; ok == true {
			f.Slacker.SimplePost(channelName, storyText, icon, false)
		}
		break
	}

	c.JSON(200, gin.H{
		"status": "success",
		"messages": []string{
			"OK",
		},
	})
}
<|endoftext|>


<commit_before>package compute

import (
	"encoding/xml"
	"errors"

	"github.com/megamsys/opennebula-go/api"
	"github.com/megamsys/opennebula-go/template"
	"github.com/megamsys/opennebula-go/virtualmachine"
)

var ErrNoVM = errors.New("no vm found, Did you launch them ?")

const (
	TEMPLATE_INSTANTIATE = "one.template.instantiate"
	ONE_VM_ACTION        = "one.vm.action"
	ONE_DISK_SNAPSHOT    = "one.vm.disksaveas"
	ONE_IMAGE_REMOVE     = "one.image.delete"
	DELETE               = "delete"
	REBOOT               = "reboot"
	POWEROFF             = "poweroff"
	RESUME               = "resume"
	ASSEMBLY_ID          = "assembly_id"
	ASSEMBLIES_ID        = "assemblies_id"
	ACCOUNTS_ID          = "accounts_id"
	PLATFORM_ID          = "platform_id"
	SSH_PUBLIC_KEY       = "SSH_PUBLIC_KEY"
)

type VirtualMachine struct {
	Name         string
	TemplateName string
	TemplateId   int
	Image        string
	ContextMap   map[string]string
	Cpu          string
	VCpu         string
	Memory       string
	HDD          string
	Region       string
	ClusterId    string
	VMId         int
	Vnets        map[string]string
	T            *api.Rpc
}

type Image struct {
	Name         string
	VMId         int
	ImageId      int
	Region       string
	T            *api.Rpc
}

// Creates a new VirtualMachine
func (v *VirtualMachine) Create() ([]interface{}, error) {
	templateObj := template.TemplateReqs{TemplateName: v.TemplateName, T: v.T}
	defer v.T.Client.Close()

	XMLtemplate, err := templateObj.Get()
	if err != nil {
		return nil, err
	}

	XMLtemplate[0].Template.Cpu = v.Cpu
	XMLtemplate[0].Template.VCpu = v.VCpu
	XMLtemplate[0].Template.Memory = v.Memory
	XMLtemplate[0].Template.Disk.Image = v.Image
	XMLtemplate[0].Template.Disk.Size = v.HDD
	XMLtemplate[0].Template.Context.Accounts_id = v.ContextMap[ACCOUNTS_ID]
	XMLtemplate[0].Template.Context.Platform_id = v.ContextMap[PLATFORM_ID]
	XMLtemplate[0].Template.Context.Assembly_id = v.ContextMap[ASSEMBLY_ID]
	XMLtemplate[0].Template.Context.Assemblies_id = v.ContextMap[ASSEMBLIES_ID]
	XMLtemplate[0].Template.Context.SSH_Public_key = v.ContextMap[SSH_PUBLIC_KEY]
	XMLtemplate[0].Template.Sched_requirments = "CLUSTER_ID=\"" + v.ClusterId + "\""

	if len(v.Vnets) > 0 {
		XMLtemplate[0].Template.Nic = XMLtemplate[0].Template.Nic[:0]
		for _, v := range v.Vnets {
			net := &template.NIC{Network: v, Network_uname: "oneadmin"}
			XMLtemplate[0].Template.Nic = append(XMLtemplate[0].Template.Nic, net)
		}
	}

	finalXML := template.UserTemplates{}
	finalXML.UserTemplate = XMLtemplate
	finalData, _ := xml.Marshal(finalXML.UserTemplate[0].Template)
	data := string(finalData)
	args := []interface{}{v.T.Key, finalXML.UserTemplate[0].Id, v.Name, false, data}
	res, err := v.T.Call(TEMPLATE_INSTANTIATE, args)
	if err != nil {
		return nil, err
	}
	return res, nil
}

/**
*
* REBoot a new virtualMachine
*
**/
func (v *VirtualMachine) Reboot() ([]interface{}, error) {

	args := []interface{}{v.T.Key, REBOOT, v.VMId}
	res, err := v.T.Call(ONE_VM_ACTION, args)
	//close connection
	defer v.T.Client.Close()
	if err != nil {
		return nil, err
	}

	return res, nil

}

/**
*
* POWEROFF a new virtualMachine
*
**/
func (v *VirtualMachine) Poweroff() ([]interface{}, error) {

	args := []interface{}{v.T.Key, POWEROFF, v.VMId}
	res, err := v.T.Call(ONE_VM_ACTION, args)
	defer v.T.Client.Close()
	if err != nil {
		return nil, err
	}

	return res, nil

}

/**
*
* Resume a new virtualMachine
*
**/
func (v *VirtualMachine) Resume() ([]interface{}, error) {

	args := []interface{}{v.T.Key, RESUME, v.VMId}
	res, err := v.T.Call(ONE_VM_ACTION, args)
	//close connection
	defer v.T.Client.Close()
	if err != nil {
		return nil, err
	}

	return res, nil

}

/**
 *
 * Deletes a new virtualMachine
 *
 **/
func (v *VirtualMachine) Delete() ([]interface{}, error) {

	args := []interface{}{v.T.Key, DELETE, v.VMId}
	res, err := v.T.Call(ONE_VM_ACTION, args)
	//close connection
	defer v.T.Client.Close()
	if err != nil {
		return nil, err
	}

	return res, nil

}

/**
 *
 * VM save as a new Image (DISK_SNAPSHOT)
 *
 **/
func (v *Image) DiskSnap() ([]interface{}, error) {

	args := []interface{}{v.T.Key, v.VMId, 0, v.Name, "", -1}
	res, err := v.T.Call(ONE_DISK_SNAPSHOT, args)
	//close connection
	defer v.T.Client.Close()
	if err != nil {
		return nil, err
	}

	return res, nil
}

func (v *Image) RemoveImage() ([]interface{}, error) {
	args := []interface{}{v.T.Key, v.ImageId}
	res, err := v.T.Call(ONE_IMAGE_REMOVE, args)
	//close connection
	defer v.T.Client.Close()
	if err != nil {
		return nil,err
	}

	return res,nil
}

func listByName(name string, client *api.Rpc) (*virtualmachine.UserVM, error) {
	vms := virtualmachine.Query{VMName: name, T: client}

	svm, err := vms.GetByName()
	if err != nil {
		return nil, err
	}

	if len(svm) <= 0 {
		return nil, ErrNoVM
	}

	if svm[0] == nil {
		return nil, ErrNoVM
	}
	return svm[0], nil
}
<commit_msg>temporary fix for vertice disksnap<commit_after>package compute

import (
	"encoding/xml"
	"errors"

	"github.com/megamsys/opennebula-go/api"
	"github.com/megamsys/opennebula-go/template"
	"github.com/megamsys/opennebula-go/virtualmachine"
)

var ErrNoVM = errors.New("no vm found, Did you launch them ?")

const (
	TEMPLATE_INSTANTIATE = "one.template.instantiate"
	ONE_VM_ACTION        = "one.vm.action"
	ONE_DISK_SNAPSHOT    = "one.vm.disksaveas"
	ONE_IMAGE_REMOVE     = "one.image.delete"
	DELETE               = "delete"
	REBOOT               = "reboot"
	POWEROFF             = "poweroff"
	RESUME               = "resume"
	ASSEMBLY_ID          = "assembly_id"
	ASSEMBLIES_ID        = "assemblies_id"
	ACCOUNTS_ID          = "accounts_id"
	PLATFORM_ID          = "platform_id"
	SSH_PUBLIC_KEY       = "SSH_PUBLIC_KEY"
)

type VirtualMachine struct {
	Name         string
	TemplateName string
	TemplateId   int
	Image        string
	ContextMap   map[string]string
	Cpu          string
	VCpu         string
	Memory       string
	HDD          string
	Region       string
	ClusterId    string
	VMId         int
	Vnets        map[string]string
	T            *api.Rpc
}

type Image struct {
	Name         string
	VMId         int
	ImageId      int
	Region       string
	T            *api.Rpc
}

// Creates a new VirtualMachine
func (v *VirtualMachine) Create() ([]interface{}, error) {
	templateObj := template.TemplateReqs{TemplateName: v.TemplateName, T: v.T}
	defer v.T.Client.Close()

	XMLtemplate, err := templateObj.Get()
	if err != nil {
		return nil, err
	}

	XMLtemplate[0].Template.Cpu = v.Cpu
	XMLtemplate[0].Template.VCpu = v.VCpu
	XMLtemplate[0].Template.Memory = v.Memory
	XMLtemplate[0].Template.Disk.Image = v.Image
	XMLtemplate[0].Template.Disk.Size = v.HDD
	XMLtemplate[0].Template.Context.Accounts_id = v.ContextMap[ACCOUNTS_ID]
	XMLtemplate[0].Template.Context.Platform_id = v.ContextMap[PLATFORM_ID]
	XMLtemplate[0].Template.Context.Assembly_id = v.ContextMap[ASSEMBLY_ID]
	XMLtemplate[0].Template.Context.Assemblies_id = v.ContextMap[ASSEMBLIES_ID]
	XMLtemplate[0].Template.Context.SSH_Public_key = v.ContextMap[SSH_PUBLIC_KEY]
	XMLtemplate[0].Template.Sched_requirments = "CLUSTER_ID=\"" + v.ClusterId + "\""

	if len(v.Vnets) > 0 {
		XMLtemplate[0].Template.Nic = XMLtemplate[0].Template.Nic[:0]
		for _, v := range v.Vnets {
			net := &template.NIC{Network: v, Network_uname: "oneadmin"}
			XMLtemplate[0].Template.Nic = append(XMLtemplate[0].Template.Nic, net)
		}
	}

	finalXML := template.UserTemplates{}
	finalXML.UserTemplate = XMLtemplate
	finalData, _ := xml.Marshal(finalXML.UserTemplate[0].Template)
	data := string(finalData)
	args := []interface{}{v.T.Key, finalXML.UserTemplate[0].Id, v.Name, false, data}
	res, err := v.T.Call(TEMPLATE_INSTANTIATE, args)
	if err != nil {
		return nil, err
	}
	return res, nil
}

/**
*
* REBoot a new virtualMachine
*
**/
func (v *VirtualMachine) Reboot() ([]interface{}, error) {

	args := []interface{}{v.T.Key, REBOOT, v.VMId}
	res, err := v.T.Call(ONE_VM_ACTION, args)
	//close connection
	defer v.T.Client.Close()
	if err != nil {
		return nil, err
	}

	return res, nil

}

/**
*
* POWEROFF a new virtualMachine
*
**/
func (v *VirtualMachine) Poweroff() ([]interface{}, error) {

	args := []interface{}{v.T.Key, POWEROFF, v.VMId}
	res, err := v.T.Call(ONE_VM_ACTION, args)
	defer v.T.Client.Close()
	if err != nil {
		return nil, err
	}

	return res, nil

}

/**
*
* Resume a new virtualMachine
*
**/
func (v *VirtualMachine) Resume() ([]interface{}, error) {

	args := []interface{}{v.T.Key, RESUME, v.VMId}
	res, err := v.T.Call(ONE_VM_ACTION, args)
	//close connection
	defer v.T.Client.Close()
	if err != nil {
		return nil, err
	}

	return res, nil

}

/**
 *
 * Deletes a new virtualMachine
 *
 **/
func (v *VirtualMachine) Delete() ([]interface{}, error) {

	args := []interface{}{v.T.Key, DELETE, v.VMId}
	res, err := v.T.Call(ONE_VM_ACTION, args)
	//close connection
	defer v.T.Client.Close()
	if err != nil {
		return nil, err
	}

	return res, nil

}

/**
 *
 * VM save as a new Image (DISK_SNAPSHOT)
 *
 **/

	func (v *VirtualMachine) DiskSnap() ([]interface{}, error) {

		args := []interface{}{v.T.Key, v.VMId, 0, v.Name, "", -1}
		res, err := v.T.Call(ONE_DISK_SNAPSHOT, args)
		//close connection
		defer v.T.Client.Close()
		if err != nil {
			return nil, err
		}

		return res, nil
	}

func (v *Image) DiskSnap() ([]interface{}, error) {

	args := []interface{}{v.T.Key, v.VMId, 0, v.Name, "", -1}
	res, err := v.T.Call(ONE_DISK_SNAPSHOT, args)
	//close connection
	defer v.T.Client.Close()
	if err != nil {
		return nil, err
	}

	return res, nil
}

func (v *Image) RemoveImage() ([]interface{}, error) {
	args := []interface{}{v.T.Key, v.ImageId}
	res, err := v.T.Call(ONE_IMAGE_REMOVE, args)
	//close connection
	defer v.T.Client.Close()
	if err != nil {
		return nil,err
	}

	return res,nil
}

func listByName(name string, client *api.Rpc) (*virtualmachine.UserVM, error) {
	vms := virtualmachine.Query{VMName: name, T: client}

	svm, err := vms.GetByName()
	if err != nil {
		return nil, err
	}

	if len(svm) <= 0 {
		return nil, ErrNoVM
	}

	if svm[0] == nil {
		return nil, ErrNoVM
	}
	return svm[0], nil
}
<|endoftext|>