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

package cmd

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

	"github.com/spf13/cobra"
	"github.com/spf13/viper"
)

// rmCmd represents the rm command
var rmCmd = &cobra.Command{
	Use:   "rm",
	Short: "Move file(s) to trash",
	Long: `Move files(s) to trash.
It requires one or more additional parameters that are file
or directory names in current working directory. Absolute
paths are supported as well.

For example:
softrm rm tempfile.txt`,
	Run: func(cmd *cobra.Command, args []string) {
		if len(args) < 1 {
			cmd.Help()
			return
		}

		// check if all arguments are valid path
		for _, argPath := range args {
			if _, err := os.Stat(argPath); os.IsNotExist(err) {
				fmt.Printf("Argument %s is not a valid path.\n", argPath)
				abortAndExit()
			}
		}
		ensureTrashDirExists()

		// create deletion instance directory
		var delInstancePath string
		{
			delInstanceName := genDeletionInstanceName()
			delInstancePath = path.Join(getTrashPath(), delInstanceName)
			err := os.MkdirAll(delInstancePath, 0700)
			if nil != err {
				fmt.Println(err.Error())
				abortAndExit()
			}
		}

		// move all files to deletion instance directory
		for _, argPath := range args {
			// create direcotry structure for new file/dir, since os.Rename can't do it
			err := os.MkdirAll(path.Join(delInstancePath, filepath.Dir(argPath)), 0700)
			if nil != err {
				fmt.Println(err.Error())
				abortAndExit()
			}

			// now move the file/dir
			if err := os.Rename(argPath, path.Join(delInstancePath, argPath)); err != nil {
				fmt.Println(err.Error())
				abortAndExit()
			}
		}

		fmt.Println("All files were moved to %s directory", delInstancePath)
		fmt.Println("rm operation successfully completed.")
	},
}

func init() {
	RootCmd.AddCommand(rmCmd)
}

// Check if trash directory exists and has adequate permissions. Create one
// if it doesn't.
func ensureTrashDirExists() {
	trashPath := getTrashPath()
	fi, err := os.Stat(trashPath)
	if err == nil {
		if fi.IsDir() {
			if 0 != strings.Compare(fi.Mode().Perm().String()[1:4], "rwx") {
				fmt.Printf("Directory %s does not have 'rwx' permissions for current user.\n", trashPath)
				abortAndExit()
			}
			return // all good
		} else {
			fmt.Printf("Node %s exists, but it's not a directory.\n", trashPath)
			abortAndExit()
		}
	}
	if os.IsNotExist(err) {
		err := os.MkdirAll(trashPath, 0700)
		if nil == err {
			fmt.Printf("Directory %s created.\n", trashPath)
			return
		} else {
			fmt.Println(err.Error())
			abortAndExit()
		}
	}
	fmt.Println("error:", err)
	abortAndExit()
}

func genDeletionInstanceName() string {
	return time.Now().Format("2006-01-02T15-04-05.999999")
}

// Trash path is currently pointing to config value.
// Later it might be more trash paths, one for each partition, to avoid
// unnecessary data transfers if source and trash path are not on
// the same partition.
func getTrashPath() string {
	return configTrashPath()
}

func configTrashPath() string {
	return os.ExpandEnv(viper.GetString("trashdir"))
}

func abortAndExit() {
	//TODO maybe replace with log.Fatal
	fmt.Println("Aborting.")
	os.Exit(1)
}
<commit_msg>Typo fix<commit_after>// Copyright © 2017 Daniel Manesku daniel.manesku@gmail.com
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cmd

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

	"github.com/spf13/cobra"
	"github.com/spf13/viper"
)

// rmCmd represents the rm command
var rmCmd = &cobra.Command{
	Use:   "rm",
	Short: "Move file(s) to trash",
	Long: `Move files(s) to trash.
It requires one or more additional parameters that are file
or directory names in current working directory. Absolute
paths are supported as well.

For example:
softrm rm tempfile.txt`,
	Run: func(cmd *cobra.Command, args []string) {
		if len(args) < 1 {
			cmd.Help()
			return
		}

		// check if all arguments are valid path
		for _, argPath := range args {
			if _, err := os.Stat(argPath); os.IsNotExist(err) {
				fmt.Printf("Argument %s is not a valid path.\n", argPath)
				abortAndExit()
			}
		}
		ensureTrashDirExists()

		// create deletion instance directory
		var delInstancePath string
		{
			delInstanceName := genDeletionInstanceName()
			delInstancePath = path.Join(getTrashPath(), delInstanceName)
			err := os.MkdirAll(delInstancePath, 0700)
			if nil != err {
				fmt.Println(err.Error())
				abortAndExit()
			}
		}

		// move all files to deletion instance directory
		for _, argPath := range args {
			// create directory structure for new file/dir, since os.Rename can't do it
			err := os.MkdirAll(path.Join(delInstancePath, filepath.Dir(argPath)), 0700)
			if nil != err {
				fmt.Println(err.Error())
				abortAndExit()
			}

			// now move the file/dir
			if err := os.Rename(argPath, path.Join(delInstancePath, argPath)); err != nil {
				fmt.Println(err.Error())
				abortAndExit()
			}
		}

		fmt.Println("All files were moved to %s directory", delInstancePath)
		fmt.Println("rm operation successfully completed.")
	},
}

func init() {
	RootCmd.AddCommand(rmCmd)
}

// Check if trash directory exists and has adequate permissions. Create one
// if it doesn't.
func ensureTrashDirExists() {
	trashPath := getTrashPath()
	fi, err := os.Stat(trashPath)
	if err == nil {
		if fi.IsDir() {
			if 0 != strings.Compare(fi.Mode().Perm().String()[1:4], "rwx") {
				fmt.Printf("Directory %s does not have 'rwx' permissions for current user.\n", trashPath)
				abortAndExit()
			}
			return // all good
		} else {
			fmt.Printf("Node %s exists, but it's not a directory.\n", trashPath)
			abortAndExit()
		}
	}
	if os.IsNotExist(err) {
		err := os.MkdirAll(trashPath, 0700)
		if nil == err {
			fmt.Printf("Directory %s created.\n", trashPath)
			return
		} else {
			fmt.Println(err.Error())
			abortAndExit()
		}
	}
	fmt.Println("error:", err)
	abortAndExit()
}

func genDeletionInstanceName() string {
	return time.Now().Format("2006-01-02T15-04-05.999999")
}

// Trash path is currently pointing to config value.
// Later it might be more trash paths, one for each partition, to avoid
// unnecessary data transfers if source and trash path are not on
// the same partition.
func getTrashPath() string {
	return configTrashPath()
}

func configTrashPath() string {
	return os.ExpandEnv(viper.GetString("trashdir"))
}

func abortAndExit() {
	//TODO maybe replace with log.Fatal
	fmt.Println("Aborting.")
	os.Exit(1)
}
<|endoftext|>


<commit_before>package memsize

import (
	"bytes"
	"fmt"
	"reflect"
	"sort"
	"strings"
	"text/tabwriter"
	"unsafe"
)

// Scan traverses all objects reachable from v and counts how much memory
// is used per type. The value must be a non-nil pointer to any value.
func Scan(v interface{}) Sizes {
	rv := reflect.ValueOf(v)
	if rv.Kind() != reflect.Ptr || rv.IsNil() {
		panic("value to scan must be non-nil pointer")
	}

	stopTheWorld("memsize scan")
	defer startTheWorld()

	ctx := newContext()
	ctx.scan(invalidAddr, rv, false)
	ctx.s.BitmapSize = ctx.seen.size()
	ctx.s.BitmapUtilization = ctx.seen.utilization()
	return *ctx.s
}

// Sizes is the result of a scan.
type Sizes struct {
	Total  uintptr
	ByType map[reflect.Type]*TypeSize
	// Internal stats (for debugging)
	BitmapSize        uintptr
	BitmapUtilization float32
}

type TypeSize struct {
	Total uintptr
	Count uintptr
}

func newSizes() *Sizes {
	return &Sizes{ByType: make(map[reflect.Type]*TypeSize)}
}

// Report returns a human-readable report.
func (s Sizes) Report() string {
	type typLine struct {
		name  string
		count uintptr
		total uintptr
	}
	tab := []typLine{{"ALL", 0, s.Total}}
	for _, typ := range s.ByType {
		tab[0].count += typ.Count
	}
	maxname := 0
	for typ, s := range s.ByType {
		line := typLine{typ.String(), s.Count, s.Total}
		tab = append(tab, line)
		if len(line.name) > maxname {
			maxname = len(line.name)
		}
	}
	sort.Slice(tab, func(i, j int) bool { return tab[i].total > tab[j].total })

	buf := new(bytes.Buffer)
	w := tabwriter.NewWriter(buf, 0, 0, 0, ' ', tabwriter.AlignRight)
	for _, line := range tab {
		namespace := strings.Repeat(" ", maxname-len(line.name))
		fmt.Fprintf(w, "%s%s\t  %v\t  %s\t\n", line.name, namespace, line.count, HumanSize(line.total))
	}
	w.Flush()
	return buf.String()
}

// addValue is called during scan and adds the memory of given object.
func (s *Sizes) addValue(v reflect.Value, size uintptr) {
	s.Total += size
	rs := s.ByType[v.Type()]
	if rs == nil {
		rs = new(TypeSize)
		s.ByType[v.Type()] = rs
	}
	rs.Total += size
	rs.Count++
}

type context struct {
	// We track previously scanned objects to prevent infinite loops
	// when scanning cycles, and to prevent scanning objects more than
	// once.
	seen *bitmap
	tc   typCache
	s    *Sizes
}

func newContext() *context {
	return &context{seen: newBitmap(), tc: make(typCache), s: newSizes()}
}

// scan walks all objects below v, determining their size. All scan* functions return the
// amount of 'extra' memory (e.g. slice data) that is referenced by the object.
func (c *context) scan(addr address, v reflect.Value, add bool) (extraSize uintptr) {
	size := v.Type().Size()
	var marked uintptr
	if addr.valid() {
		marked = c.seen.countRange(uintptr(addr), size)
		if marked == size {
			return 0 // Skip if we have already seen the whole object.
		}
		c.seen.markRange(uintptr(addr), size)
	}
	// fmt.Printf("%v: %v ⮑ (marked %d)\n", addr, v.Type(), marked)
	if c.tc.needScan(v.Type()) {
		extraSize = c.scanContent(addr, v)
	}
	// fmt.Printf("%v: %v %d (add %v, size %d, marked %d, extra %d)\n", addr, v.Type(), size+extraSize, add, v.Type().Size(), marked, extraSize)
	if add {
		size -= marked
		size += extraSize
		c.s.addValue(v, size)
	}
	return extraSize
}

func (c *context) scanContent(addr address, v reflect.Value) uintptr {
	switch v.Kind() {
	case reflect.Array:
		return c.scanArray(addr, v)
	case reflect.Chan:
		return c.scanChan(v)
	case reflect.Func:
		// can't do anything here
		return 0
	case reflect.Interface:
		return c.scanInterface(v)
	case reflect.Map:
		return c.scanMap(v)
	case reflect.Ptr:
		if !v.IsNil() {
			c.scan(address(v.Pointer()), v.Elem(), true)
		}
		return 0
	case reflect.Slice:
		return c.scanSlice(v)
	case reflect.String:
		return uintptr(v.Len())
	case reflect.Struct:
		return c.scanStruct(addr, v)
	default:
		unhandledKind(v.Kind())
		return 0
	}
}

func (c *context) scanChan(v reflect.Value) uintptr {
	etyp := v.Type().Elem()
	extra := uintptr(0)
	if c.tc.needScan(etyp) {
		// Scan the channel buffer. This is unsafe but doesn't race because
		// the world is stopped during scan.
		hchan := unsafe.Pointer(v.Pointer())
		for i := uint(0); i < uint(v.Cap()); i++ {
			addr := chanbuf(hchan, i)
			elem := reflect.NewAt(etyp, addr).Elem()
			extra += c.scanContent(address(addr), elem)
		}
	}
	return uintptr(v.Cap())*etyp.Size() + extra
}

func (c *context) scanStruct(base address, v reflect.Value) uintptr {
	extra := uintptr(0)
	for i := 0; i < v.NumField(); i++ {
		f := v.Type().Field(i)
		if c.tc.needScan(f.Type) {
			addr := base.addOffset(f.Offset)
			extra += c.scanContent(addr, v.Field(i))
		}
	}
	return extra
}

func (c *context) scanArray(addr address, v reflect.Value) uintptr {
	esize := v.Type().Elem().Size()
	extra := uintptr(0)
	if c.tc.needScan(v.Type().Elem()) {
		for i := 0; i < v.Len(); i++ {
			extra += c.scanContent(addr, v.Index(i))
			addr = addr.addOffset(esize)
		}
	}
	return extra
}

func (c *context) scanSlice(v reflect.Value) uintptr {
	slice := v.Slice(0, v.Cap())
	esize := slice.Type().Elem().Size()
	base := slice.Pointer()
	// Add size of the unscanned portion of the backing array to extra.
	blen := uintptr(slice.Len()) * esize
	marked := c.seen.countRange(base, blen)
	extra := blen - marked
	c.seen.markRange(uintptr(base), blen)
	if c.tc.needScan(slice.Type().Elem()) {
		// Elements may contain pointers, scan them individually.
		addr := address(base)
		for i := 0; i < slice.Len(); i++ {
			extra += c.scanContent(addr, slice.Index(i))
			addr = addr.addOffset(esize)
		}
	}
	return extra
}

func (c *context) scanMap(v reflect.Value) uintptr {
	var (
		typ   = v.Type()
		len   = uintptr(v.Len())
		extra = uintptr(0)
	)
	if c.tc.needScan(typ.Key()) || c.tc.needScan(typ.Elem()) {
		for _, k := range v.MapKeys() {
			extra += c.scan(invalidAddr, k, false)
			extra += c.scan(invalidAddr, v.MapIndex(k), false)
		}
	}
	return len*typ.Key().Size() + len*typ.Elem().Size() + extra
}

func (c *context) scanInterface(v reflect.Value) uintptr {
	elem := v.Elem()
	if !elem.IsValid() {
		return 0 // nil interface
	}
	c.scan(invalidAddr, elem, false)
	if !c.tc.isPointer(elem.Type()) {
		// Account for non-pointer size of the value.
		return elem.Type().Size()
	}
	return 0
}
<commit_msg>memsize: remove redundant check for needScan in scanArray<commit_after>package memsize

import (
	"bytes"
	"fmt"
	"reflect"
	"sort"
	"strings"
	"text/tabwriter"
	"unsafe"
)

// Scan traverses all objects reachable from v and counts how much memory
// is used per type. The value must be a non-nil pointer to any value.
func Scan(v interface{}) Sizes {
	rv := reflect.ValueOf(v)
	if rv.Kind() != reflect.Ptr || rv.IsNil() {
		panic("value to scan must be non-nil pointer")
	}

	stopTheWorld("memsize scan")
	defer startTheWorld()

	ctx := newContext()
	ctx.scan(invalidAddr, rv, false)
	ctx.s.BitmapSize = ctx.seen.size()
	ctx.s.BitmapUtilization = ctx.seen.utilization()
	return *ctx.s
}

// Sizes is the result of a scan.
type Sizes struct {
	Total  uintptr
	ByType map[reflect.Type]*TypeSize
	// Internal stats (for debugging)
	BitmapSize        uintptr
	BitmapUtilization float32
}

type TypeSize struct {
	Total uintptr
	Count uintptr
}

func newSizes() *Sizes {
	return &Sizes{ByType: make(map[reflect.Type]*TypeSize)}
}

// Report returns a human-readable report.
func (s Sizes) Report() string {
	type typLine struct {
		name  string
		count uintptr
		total uintptr
	}
	tab := []typLine{{"ALL", 0, s.Total}}
	for _, typ := range s.ByType {
		tab[0].count += typ.Count
	}
	maxname := 0
	for typ, s := range s.ByType {
		line := typLine{typ.String(), s.Count, s.Total}
		tab = append(tab, line)
		if len(line.name) > maxname {
			maxname = len(line.name)
		}
	}
	sort.Slice(tab, func(i, j int) bool { return tab[i].total > tab[j].total })

	buf := new(bytes.Buffer)
	w := tabwriter.NewWriter(buf, 0, 0, 0, ' ', tabwriter.AlignRight)
	for _, line := range tab {
		namespace := strings.Repeat(" ", maxname-len(line.name))
		fmt.Fprintf(w, "%s%s\t  %v\t  %s\t\n", line.name, namespace, line.count, HumanSize(line.total))
	}
	w.Flush()
	return buf.String()
}

// addValue is called during scan and adds the memory of given object.
func (s *Sizes) addValue(v reflect.Value, size uintptr) {
	s.Total += size
	rs := s.ByType[v.Type()]
	if rs == nil {
		rs = new(TypeSize)
		s.ByType[v.Type()] = rs
	}
	rs.Total += size
	rs.Count++
}

type context struct {
	// We track previously scanned objects to prevent infinite loops
	// when scanning cycles and to prevent counting objects more than once.
	seen *bitmap
	tc   typCache
	s    *Sizes
}

func newContext() *context {
	return &context{seen: newBitmap(), tc: make(typCache), s: newSizes()}
}

// scan walks all objects below v, determining their size. All scan* functions return the
// amount of 'extra' memory (e.g. slice data) that is referenced by the object.
func (c *context) scan(addr address, v reflect.Value, add bool) (extraSize uintptr) {
	size := v.Type().Size()
	var marked uintptr
	if addr.valid() {
		marked = c.seen.countRange(uintptr(addr), size)
		if marked == size {
			return 0 // Skip if we have already seen the whole object.
		}
		c.seen.markRange(uintptr(addr), size)
	}
	// fmt.Printf("%v: %v ⮑ (marked %d)\n", addr, v.Type(), marked)
	if c.tc.needScan(v.Type()) {
		extraSize = c.scanContent(addr, v)
	}
	// fmt.Printf("%v: %v %d (add %v, size %d, marked %d, extra %d)\n", addr, v.Type(), size+extraSize, add, v.Type().Size(), marked, extraSize)
	if add {
		size -= marked
		size += extraSize
		c.s.addValue(v, size)
	}
	return extraSize
}

func (c *context) scanContent(addr address, v reflect.Value) uintptr {
	switch v.Kind() {
	case reflect.Array:
		return c.scanArray(addr, v)
	case reflect.Chan:
		return c.scanChan(v)
	case reflect.Func:
		// can't do anything here
		return 0
	case reflect.Interface:
		return c.scanInterface(v)
	case reflect.Map:
		return c.scanMap(v)
	case reflect.Ptr:
		if !v.IsNil() {
			c.scan(address(v.Pointer()), v.Elem(), true)
		}
		return 0
	case reflect.Slice:
		return c.scanSlice(v)
	case reflect.String:
		return uintptr(v.Len())
	case reflect.Struct:
		return c.scanStruct(addr, v)
	default:
		unhandledKind(v.Kind())
		return 0
	}
}

func (c *context) scanChan(v reflect.Value) uintptr {
	etyp := v.Type().Elem()
	extra := uintptr(0)
	if c.tc.needScan(etyp) {
		// Scan the channel buffer. This is unsafe but doesn't race because
		// the world is stopped during scan.
		hchan := unsafe.Pointer(v.Pointer())
		for i := uint(0); i < uint(v.Cap()); i++ {
			addr := chanbuf(hchan, i)
			elem := reflect.NewAt(etyp, addr).Elem()
			extra += c.scanContent(address(addr), elem)
		}
	}
	return uintptr(v.Cap())*etyp.Size() + extra
}

func (c *context) scanStruct(base address, v reflect.Value) uintptr {
	extra := uintptr(0)
	for i := 0; i < v.NumField(); i++ {
		f := v.Type().Field(i)
		if c.tc.needScan(f.Type) {
			addr := base.addOffset(f.Offset)
			extra += c.scanContent(addr, v.Field(i))
		}
	}
	return extra
}

func (c *context) scanArray(addr address, v reflect.Value) uintptr {
	esize := v.Type().Elem().Size()
	extra := uintptr(0)
	for i := 0; i < v.Len(); i++ {
		extra += c.scanContent(addr, v.Index(i))
		addr = addr.addOffset(esize)
	}
	return extra
}

func (c *context) scanSlice(v reflect.Value) uintptr {
	slice := v.Slice(0, v.Cap())
	esize := slice.Type().Elem().Size()
	base := slice.Pointer()
	// Add size of the unscanned portion of the backing array to extra.
	blen := uintptr(slice.Len()) * esize
	marked := c.seen.countRange(base, blen)
	extra := blen - marked
	c.seen.markRange(uintptr(base), blen)
	if c.tc.needScan(slice.Type().Elem()) {
		// Elements may contain pointers, scan them individually.
		addr := address(base)
		for i := 0; i < slice.Len(); i++ {
			extra += c.scanContent(addr, slice.Index(i))
			addr = addr.addOffset(esize)
		}
	}
	return extra
}

func (c *context) scanMap(v reflect.Value) uintptr {
	var (
		typ   = v.Type()
		len   = uintptr(v.Len())
		extra = uintptr(0)
	)
	if c.tc.needScan(typ.Key()) || c.tc.needScan(typ.Elem()) {
		for _, k := range v.MapKeys() {
			extra += c.scan(invalidAddr, k, false)
			extra += c.scan(invalidAddr, v.MapIndex(k), false)
		}
	}
	return len*typ.Key().Size() + len*typ.Elem().Size() + extra
}

func (c *context) scanInterface(v reflect.Value) uintptr {
	elem := v.Elem()
	if !elem.IsValid() {
		return 0 // nil interface
	}
	c.scan(invalidAddr, elem, false)
	if !c.tc.isPointer(elem.Type()) {
		// Account for non-pointer size of the value.
		return elem.Type().Size()
	}
	return 0
}
<|endoftext|>


<commit_before>package main

import (
	"math/rand"
	"strings"
)

// Message describes a message we can send to the queue
type Message struct {
	Name       string
	Exchange   string
	RoutingKey string
	Properties []MessageProperty
}

// MessageProperty describes a property of a message
type MessageProperty struct {
	Name         string
	DataType     string
	DefaultValue string
}

// MessageContent provides a dynamic structure for json-ifying messages
type MessageContent map[string]interface{}

// GenerateContent returns a string of the json-encoded message with dynamic properties
func (m *Message) GenerateContent() MessageContent {

	data := make(map[string]interface{})

	for _, property := range m.Properties {
		gen := getGenerator(property)
		data[property.Name] = gen()
	}

	return data
}

func getGenerator(prop MessageProperty) func() interface{} {
	if !strings.HasPrefix(prop.DefaultValue, "_GENERATE") {
		return func() interface{} { return prop.DefaultValue }
	}

	switch prop.DefaultValue {
	case "_GENERATE_STRING":
		return generateString
	case "_GENERATE_INT":
		return generateInt
	default:
		return func() interface{} { return "" }
	}
}

func generateString() interface{} {
	var letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
	b := make([]rune, 8)
	for i := range b {
		b[i] = letterRunes[rand.Intn(len(letterRunes))]
	}
	return string(b)
}

func generateInt() interface{} {
	return rand.Int()
}
<commit_msg>Add comment for source of generateString<commit_after>package main

import (
	"math/rand"
	"strings"
)

// Message describes a message we can send to the queue
type Message struct {
	Name       string
	Exchange   string
	RoutingKey string
	Properties []MessageProperty
}

// MessageProperty describes a property of a message
type MessageProperty struct {
	Name         string
	DataType     string
	DefaultValue string
}

// MessageContent provides a dynamic structure for json-ifying messages
type MessageContent map[string]interface{}

// GenerateContent returns a string of the json-encoded message with dynamic properties
func (m *Message) GenerateContent() MessageContent {

	data := make(map[string]interface{})

	for _, property := range m.Properties {
		gen := getGenerator(property)
		data[property.Name] = gen()
	}

	return data
}

func getGenerator(prop MessageProperty) func() interface{} {
	if !strings.HasPrefix(prop.DefaultValue, "_GENERATE") {
		return func() interface{} { return prop.DefaultValue }
	}

	switch prop.DefaultValue {
	case "_GENERATE_STRING":
		return generateString
	case "_GENERATE_INT":
		return generateInt
	default:
		return func() interface{} { return "" }
	}
}

// Adapted from http://stackoverflow.com/questions/22892120/how-to-generate-a-random-string-of-a-fixed-length-in-golang
func generateString() interface{} {
	var letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
	b := make([]rune, 8)
	for i := range b {
		b[i] = letterRunes[rand.Intn(len(letterRunes))]
	}
	return string(b)
}

func generateInt() interface{} {
	return rand.Int()
}
<|endoftext|>


<commit_before>package osc

import (
	"bytes"
	"encoding/binary"
	"errors"
	"fmt"
	"time"
)

// Packet is the interface for Message and Bundle.
type Packet interface {
	ToByteArray() (buffer []byte, err error)
}

// Represents a single OSC message. An OSC message consists of an OSC address
// pattern and zero or more arguments.
type Message struct {
	Address   string
	Arguments []interface{}
}

// An OSC Bundle consists of the OSC-string "#bundle" followed by an OSC Time Tag,
// followed by zero or more OSC bundle/message elements. The OSC-timetag is a 64-bit fixed
// point time tag. See http://opensoundcontrol.org/spec-1_0 for more information.
type Bundle struct {
	Timetag  Timetag
	Messages []*Message
	Bundles  []*Bundle
}

// NewMessage returns a new Message. The address parameter is the OSC address.
func NewMessage(address string) (msg *Message) {
	return &Message{Address: address}
}

// Append appends the given argument to the arguments list.
func (msg *Message) Append(argument interface{}) {
	msg.Arguments = append(msg.Arguments, argument)
}

// Equals determines if the given OSC Message b is equal to the current OSC Message.
// It checks if the OSC address and the arguments are equal. Returns, true if the
// current object and b are equal.
func (msg *Message) Equals(b *Message) bool {
	// Check OSC address
	if msg.Address != b.Address {
		return false
	}

	// Check if the number of arguments are equal
	if msg.CountArguments() != b.CountArguments() {
		return false
	}

	// Check arguments
	for i, arg := range msg.Arguments {
		switch arg.(type) {
		case bool, int32, int64, float32, float64, string:
			if arg != b.Arguments[i] {
				return false
			}

		case []byte:
			ba := arg.([]byte)
			bb := b.Arguments[i].([]byte)
			if !bytes.Equal(ba, bb) {
				return false
			}

		case Timetag:
			if arg.(Timetag) != b.Arguments[i].(Timetag) {
				return false
			}
		}
	}

	return true
}

// Clear clears the OSC address and all arguments.
func (msg *Message) Clear() {
	msg.Address = ""
	msg.ClearData()
}

// ClearData removes all arguments from the OSC Message.
func (msg *Message) ClearData() {
	msg.Arguments = msg.Arguments[len(msg.Arguments):]
}

// Returns true, if the address of the OSC Message matches the given address.
// Case sensitive!
func (msg *Message) Match(address string) bool {
	exp := getRegEx(msg.Address)

	if exp.MatchString(address) {
		return true
	}

	return false
}

// TypeTags returns the type tag string.
func (msg *Message) TypeTags() (tags string, err error) {
	tags = ","
	for _, m := range msg.Arguments {
		s, err := getTypeTag(m)
		if err != nil {
			return "", err
		}
		tags += s
	}

	return tags, nil
}

// CountArguments returns the number of arguments.
func (msg *Message) CountArguments() int {
	return len(msg.Arguments)
}

// ToByteBuffer serializes the OSC message to a byte buffer. The byte buffer
// is of the following format:
// 1. OSC Address Pattern
// 2. OSC Type Tag String
// 3. OSC Arguments
func (msg *Message) ToByteArray() (buffer []byte, err error) {
	// The byte buffer for the message
	var data = new(bytes.Buffer)

	// We can start with the OSC address and add it to the buffer
	_, err = writePaddedString(msg.Address, data)
	if err != nil {
		return nil, err
	}

	// Type tag string starts with ","
	typetags := []byte{','}

	// Process the type tags and collect all arguments
	var payload = new(bytes.Buffer)
	for _, arg := range msg.Arguments {
		// FIXME: Use t instead of arg
		switch t := arg.(type) {
		default:
			return nil, errors.New(fmt.Sprintf("OSC - unsupported type: %T", t))

		case bool:
			if arg.(bool) == true {
				typetags = append(typetags, 'T')
			} else {
				typetags = append(typetags, 'F')
			}

		case nil:
			typetags = append(typetags, 'N')

		case int32:
			typetags = append(typetags, 'i')

			if err = binary.Write(payload, binary.BigEndian, int32(t)); err != nil {
				return nil, err
			}

		case float32:
			typetags = append(typetags, 'f')

			if err = binary.Write(payload, binary.BigEndian, float32(t)); err != nil {
				return nil, err
			}

		case string:
			typetags = append(typetags, 's')

			if _, err = writePaddedString(t, payload); err != nil {
				return nil, err
			}

		case []byte:
			typetags = append(typetags, 'b')

			if _, err = writeBlob(t, payload); err != nil {
				return nil, err
			}

		case int64:
			typetags = append(typetags, 'h')

			if err = binary.Write(payload, binary.BigEndian, int64(t)); err != nil {
				return nil, err
			}

		case float64:
			typetags = append(typetags, 'd')

			if err = binary.Write(payload, binary.BigEndian, float64(t)); err != nil {
				return nil, err
			}

		case Timetag:
			typetags = append(typetags, 't')

			timeTag := arg.(Timetag)
			payload.Write(timeTag.ToByteArray())
		}
	}

	// Write the type tag string to the data buffer
	_, err = writePaddedString(string(typetags), data)
	if err != nil {
		return nil, err
	}

	// Write the payload (OSC arguments) to the data buffer
	data.Write(payload.Bytes())

	return data.Bytes(), nil
}

////
// Bundle
////

// NewBundle returns an OSC Bundle. Use this function to create a new OSC
// Bundle.
func NewBundle(time time.Time) (bundle *Bundle) {
	return &Bundle{Timetag: NewTimetag(time)}
}

// Append appends an OSC bundle or OSC message to the bundle.
func (self *Bundle) Append(pkt Packet) (err error) {
	switch t := pkt.(type) {
	default:
		return errors.New(fmt.Sprintf("Unsupported OSC packet type: only Bundle and Message are supported.", t))

	case *Bundle:
		self.Bundles = append(self.Bundles, t)

	case *Message:
		self.Messages = append(self.Messages, t)
	}

	return nil
}

// ToByteArray serializes the OSC bundle to a byte array with the following format:
// 1. Bundle string: '#bundle'
// 2. OSC timetag
// 3. Length of first OSC bundle element
// 4. First bundle element
// 5. Length of n OSC bundle element
// 6. n bundle element
func (self *Bundle) ToByteArray() (buffer []byte, err error) {
	var data = new(bytes.Buffer)

	// Add the '#bundle' string
	_, err = writePaddedString("#bundle", data)
	if err != nil {
		return nil, err
	}

	// Add the timetag
	if _, err = data.Write(self.Timetag.ToByteArray()); err != nil {
		return nil, err
	}

	// Process all OSC Messages
	for _, m := range self.Messages {
		var msgLen int
		var msgBuf []byte

		msgBuf, err = m.ToByteArray()
		if err != nil {
			return nil, err
		}

		// Append the length of the OSC message
		msgLen = len(msgBuf)
		if err = binary.Write(data, binary.BigEndian, int32(msgLen)); err != nil {
			return nil, err
		}

		// Append the OSC message
		data.Write(msgBuf)
	}

	// Process all OSC Bundles
	for _, b := range self.Bundles {
		var bLen int
		var bBuf []byte

		bBuf, err = b.ToByteArray()
		if err != nil {
			return nil, err
		}

		// Write the size of the bundle
		bLen = len(bBuf)
		if err = binary.Write(data, binary.BigEndian, int32(bLen)); err != nil {
			return nil, err
		}

		// Append the bundle
		data.Write(bBuf)
	}

	return data.Bytes(), nil
}

// PrintMessages pretty prints an Message to the standard output.
func PrintMessage(msg *Message) {
	tags, err := msg.TypeTags()
	if err != nil {
		return
	}

	var formatString string
	var arguments []interface{}
	formatString += "%s %s"
	arguments = append(arguments, msg.Address)
	arguments = append(arguments, tags)

	for _, arg := range msg.Arguments {
		switch arg.(type) {
		case bool, int32, int64, float32, float64, string:
			formatString += " %v"
			arguments = append(arguments, arg)

		case nil:
			formatString += " %s"
			arguments = append(arguments, "Nil")

		case []byte:
			formatString += " %s"
			arguments = append(arguments, "blob")

		case Timetag:
			formatString += " %d"
			timeTag := arg.(Timetag)
			arguments = append(arguments, uint64(timeTag))
		}
	}
	fmt.Println(fmt.Sprintf(formatString, arguments...))
}
<commit_msg>make message Append method variadic<commit_after>package osc

import (
	"bytes"
	"encoding/binary"
	"errors"
	"fmt"
	"time"
)

// Packet is the interface for Message and Bundle.
type Packet interface {
	ToByteArray() (buffer []byte, err error)
}

// Represents a single OSC message. An OSC message consists of an OSC address
// pattern and zero or more arguments.
type Message struct {
	Address   string
	Arguments []interface{}
}

// An OSC Bundle consists of the OSC-string "#bundle" followed by an OSC Time Tag,
// followed by zero or more OSC bundle/message elements. The OSC-timetag is a 64-bit fixed
// point time tag. See http://opensoundcontrol.org/spec-1_0 for more information.
type Bundle struct {
	Timetag  Timetag
	Messages []*Message
	Bundles  []*Bundle
}

// NewMessage returns a new Message. The address parameter is the OSC address.
func NewMessage(address string) (msg *Message) {
	return &Message{Address: address}
}

// Append appends the given argument to the arguments list.
func (msg *Message) Append(arguments ...interface{}) {
	msg.Arguments = append(msg.Arguments, arguments...)
}

// Equals determines if the given OSC Message b is equal to the current OSC Message.
// It checks if the OSC address and the arguments are equal. Returns, true if the
// current object and b are equal.
func (msg *Message) Equals(b *Message) bool {
	// Check OSC address
	if msg.Address != b.Address {
		return false
	}

	// Check if the number of arguments are equal
	if msg.CountArguments() != b.CountArguments() {
		return false
	}

	// Check arguments
	for i, arg := range msg.Arguments {
		switch arg.(type) {
		case bool, int32, int64, float32, float64, string:
			if arg != b.Arguments[i] {
				return false
			}

		case []byte:
			ba := arg.([]byte)
			bb := b.Arguments[i].([]byte)
			if !bytes.Equal(ba, bb) {
				return false
			}

		case Timetag:
			if arg.(Timetag) != b.Arguments[i].(Timetag) {
				return false
			}
		}
	}

	return true
}

// Clear clears the OSC address and all arguments.
func (msg *Message) Clear() {
	msg.Address = ""
	msg.ClearData()
}

// ClearData removes all arguments from the OSC Message.
func (msg *Message) ClearData() {
	msg.Arguments = msg.Arguments[len(msg.Arguments):]
}

// Returns true, if the address of the OSC Message matches the given address.
// Case sensitive!
func (msg *Message) Match(address string) bool {
	exp := getRegEx(msg.Address)

	if exp.MatchString(address) {
		return true
	}

	return false
}

// TypeTags returns the type tag string.
func (msg *Message) TypeTags() (tags string, err error) {
	tags = ","
	for _, m := range msg.Arguments {
		s, err := getTypeTag(m)
		if err != nil {
			return "", err
		}
		tags += s
	}

	return tags, nil
}

// CountArguments returns the number of arguments.
func (msg *Message) CountArguments() int {
	return len(msg.Arguments)
}

// ToByteBuffer serializes the OSC message to a byte buffer. The byte buffer
// is of the following format:
// 1. OSC Address Pattern
// 2. OSC Type Tag String
// 3. OSC Arguments
func (msg *Message) ToByteArray() (buffer []byte, err error) {
	// The byte buffer for the message
	var data = new(bytes.Buffer)

	// We can start with the OSC address and add it to the buffer
	_, err = writePaddedString(msg.Address, data)
	if err != nil {
		return nil, err
	}

	// Type tag string starts with ","
	typetags := []byte{','}

	// Process the type tags and collect all arguments
	var payload = new(bytes.Buffer)
	for _, arg := range msg.Arguments {
		// FIXME: Use t instead of arg
		switch t := arg.(type) {
		default:
			return nil, errors.New(fmt.Sprintf("OSC - unsupported type: %T", t))

		case bool:
			if arg.(bool) == true {
				typetags = append(typetags, 'T')
			} else {
				typetags = append(typetags, 'F')
			}

		case nil:
			typetags = append(typetags, 'N')

		case int32:
			typetags = append(typetags, 'i')

			if err = binary.Write(payload, binary.BigEndian, int32(t)); err != nil {
				return nil, err
			}

		case float32:
			typetags = append(typetags, 'f')

			if err = binary.Write(payload, binary.BigEndian, float32(t)); err != nil {
				return nil, err
			}

		case string:
			typetags = append(typetags, 's')

			if _, err = writePaddedString(t, payload); err != nil {
				return nil, err
			}

		case []byte:
			typetags = append(typetags, 'b')

			if _, err = writeBlob(t, payload); err != nil {
				return nil, err
			}

		case int64:
			typetags = append(typetags, 'h')

			if err = binary.Write(payload, binary.BigEndian, int64(t)); err != nil {
				return nil, err
			}

		case float64:
			typetags = append(typetags, 'd')

			if err = binary.Write(payload, binary.BigEndian, float64(t)); err != nil {
				return nil, err
			}

		case Timetag:
			typetags = append(typetags, 't')

			timeTag := arg.(Timetag)
			payload.Write(timeTag.ToByteArray())
		}
	}

	// Write the type tag string to the data buffer
	_, err = writePaddedString(string(typetags), data)
	if err != nil {
		return nil, err
	}

	// Write the payload (OSC arguments) to the data buffer
	data.Write(payload.Bytes())

	return data.Bytes(), nil
}

////
// Bundle
////

// NewBundle returns an OSC Bundle. Use this function to create a new OSC
// Bundle.
func NewBundle(time time.Time) (bundle *Bundle) {
	return &Bundle{Timetag: NewTimetag(time)}
}

// Append appends an OSC bundle or OSC message to the bundle.
func (self *Bundle) Append(pkt Packet) (err error) {
	switch t := pkt.(type) {
	default:
		return errors.New(fmt.Sprintf("Unsupported OSC packet type: only Bundle and Message are supported.", t))

	case *Bundle:
		self.Bundles = append(self.Bundles, t)

	case *Message:
		self.Messages = append(self.Messages, t)
	}

	return nil
}

// ToByteArray serializes the OSC bundle to a byte array with the following format:
// 1. Bundle string: '#bundle'
// 2. OSC timetag
// 3. Length of first OSC bundle element
// 4. First bundle element
// 5. Length of n OSC bundle element
// 6. n bundle element
func (self *Bundle) ToByteArray() (buffer []byte, err error) {
	var data = new(bytes.Buffer)

	// Add the '#bundle' string
	_, err = writePaddedString("#bundle", data)
	if err != nil {
		return nil, err
	}

	// Add the timetag
	if _, err = data.Write(self.Timetag.ToByteArray()); err != nil {
		return nil, err
	}

	// Process all OSC Messages
	for _, m := range self.Messages {
		var msgLen int
		var msgBuf []byte

		msgBuf, err = m.ToByteArray()
		if err != nil {
			return nil, err
		}

		// Append the length of the OSC message
		msgLen = len(msgBuf)
		if err = binary.Write(data, binary.BigEndian, int32(msgLen)); err != nil {
			return nil, err
		}

		// Append the OSC message
		data.Write(msgBuf)
	}

	// Process all OSC Bundles
	for _, b := range self.Bundles {
		var bLen int
		var bBuf []byte

		bBuf, err = b.ToByteArray()
		if err != nil {
			return nil, err
		}

		// Write the size of the bundle
		bLen = len(bBuf)
		if err = binary.Write(data, binary.BigEndian, int32(bLen)); err != nil {
			return nil, err
		}

		// Append the bundle
		data.Write(bBuf)
	}

	return data.Bytes(), nil
}

// PrintMessages pretty prints an Message to the standard output.
func PrintMessage(msg *Message) {
	tags, err := msg.TypeTags()
	if err != nil {
		return
	}

	var formatString string
	var arguments []interface{}
	formatString += "%s %s"
	arguments = append(arguments, msg.Address)
	arguments = append(arguments, tags)

	for _, arg := range msg.Arguments {
		switch arg.(type) {
		case bool, int32, int64, float32, float64, string:
			formatString += " %v"
			arguments = append(arguments, arg)

		case nil:
			formatString += " %s"
			arguments = append(arguments, "Nil")

		case []byte:
			formatString += " %s"
			arguments = append(arguments, "blob")

		case Timetag:
			formatString += " %d"
			timeTag := arg.(Timetag)
			arguments = append(arguments, uint64(timeTag))
		}
	}
	fmt.Println(fmt.Sprintf(formatString, arguments...))
}
<|endoftext|>


<commit_before>package dbus

import (
	"bytes"
	"encoding/binary"
	"io"
	"reflect"
)

const protoVersion byte = 1

// Flags represents the possible flags of a DBus message.
type Flags byte

const (
	NoReplyExpected Flags = 1 << iota
	NoAutoStart
)

// Type represents the possible types of a DBus message.
type Type byte

const (
	TypeMethodCall Type = 1 + iota
	TypeMethodReply
	TypeError
	TypeSignal
	typeMax
)

// HeaderField represents the possible byte codes for the headers
// of a DBus message.
type HeaderField byte

const (
	FieldPath HeaderField = 1 + iota
	FieldInterface
	FieldMember
	FieldErrorName
	FieldReplySerial
	FieldDestination
	FieldSender
	FieldSignature
	FieldUnixFds
	fieldMax
)

// An InvalidMessageError describes the reason why a DBus message is regarded as
// invalid.
type InvalidMessageError string

func (e InvalidMessageError) Error() string {
	return "invalid message: " + string(e)
}

var fieldTypes = map[HeaderField]reflect.Type{
	FieldPath:        objectPathType,
	FieldInterface:   stringType,
	FieldMember:      stringType,
	FieldErrorName:   stringType,
	FieldReplySerial: uint32Type,
	FieldDestination: stringType,
	FieldSender:      stringType,
	FieldSignature:   signatureType,
	FieldUnixFds:     uint32Type,
}

var requiredFields = map[Type][]HeaderField{
	TypeMethodCall:  []HeaderField{FieldPath, FieldMember},
	TypeMethodReply: []HeaderField{FieldReplySerial},
	TypeError:       []HeaderField{FieldErrorName, FieldReplySerial},
	TypeSignal:      []HeaderField{FieldPath, FieldInterface, FieldMember},
}

// Message represents a single DBus message.
type Message struct {
	// must be binary.BigEndian or binary.LittleEndian
	Order binary.ByteOrder

	Type
	Flags
	Serial  uint32
	Headers map[HeaderField]Variant
	Body    []byte
}

type header struct {
	HeaderField
	Variant
}

// DecodeMessage tries to decode a single message from the given reader.
// The byte order is figured out from the first byte. The possibly returned
// error may either be an error of the underlying reader or an
// InvalidMessageError.
func DecodeMessage(rd io.Reader) (message *Message, err error) {
	var order binary.ByteOrder
	var length uint32
	var proto byte
	var headers []header

	b := make([]byte, 1)
	_, err = rd.Read(b)
	if err != nil {
		return
	}
	switch b[0] {
	case 'l':
		order = binary.LittleEndian
	case 'B':
		order = binary.BigEndian
	default:
		return nil, InvalidMessageError("invalid byte order")
	}

	dec := NewDecoder(rd, order)
	dec.pos = 1

	message = new(Message)
	message.Order = order
	err = dec.DecodeMulti(&message.Type, &message.Flags, &proto, &length,
		&message.Serial, &headers)
	if err != nil {
		return nil, err
	}

	message.Headers = make(map[HeaderField]Variant)
	for _, v := range headers {
		message.Headers[v.HeaderField] = v.Variant
	}

	dec.align(8)
	message.Body = make([]byte, int(length))
	if length != 0 {
		_, err := rd.Read(message.Body)
		if err != nil {
			return nil, err
		}
	}

	if err = message.IsValid(); err != nil {
		return nil, err
	}

	return
}

// EncodeTo encodes and sends a message to the given writer. If the message is
// not valid or an error occurs when writing, an error is returned.
func (message *Message) EncodeTo(out io.Writer) error {
	if err := message.IsValid(); err != nil {
		return err
	}
	vs := make([]interface{}, 7)
	switch message.Order {
	case binary.LittleEndian:
		vs[0] = byte('l')
	case binary.BigEndian:
		vs[0] = byte('B')
	}
	vs[1] = message.Type
	vs[2] = message.Flags
	vs[3] = protoVersion
	vs[4] = uint32(len(message.Body))
	vs[5] = message.Serial
	headers := make([]header, 0)
	for k, v := range message.Headers {
		headers = append(headers, header{k, v})
	}
	vs[6] = headers
	buf := new(bytes.Buffer)
	enc := NewEncoder(buf, binary.LittleEndian)
	enc.EncodeMulti(vs...)
	enc.align(8)
	if len(message.Body) != 0 {
		buf.Write(message.Body)
	}
	if _, err := buf.WriteTo(out); err != nil {
		return err
	}
	return nil
}

// IsValid checks whether message is a valid message and returns an
// InvalidMessageError if it is not.
func (message *Message) IsValid() error {
	switch message.Order {
	case binary.LittleEndian, binary.BigEndian:
	default:
		return InvalidMessageError("invalid byte order")
	}
	if message.Flags & ^(NoAutoStart|NoReplyExpected) != 0 {
		return InvalidMessageError("invalid flags")
	}
	if message.Type == 0 || message.Type >= typeMax {
		return InvalidMessageError("invalid message type")
	}
	for k, v := range message.Headers {
		if k == 0 || k >= fieldMax {
			return InvalidMessageError("invalid header")
		}
		if reflect.TypeOf(v.value) != fieldTypes[k] {
			return InvalidMessageError("invalid type of header field")
		}
	}
	for _, v := range requiredFields[message.Type] {
		if _, ok := message.Headers[v]; !ok {
			return InvalidMessageError("missing required header")
		}
	}
	if path, ok := message.Headers[FieldPath]; ok {
		if !path.value.(ObjectPath).IsValid() {
			return InvalidMessageError("invalid path")
		}
	}
	if len(message.Body) != 0 {
		if _, ok := message.Headers[FieldSignature]; !ok {
			return InvalidMessageError("missing signature")
		}
	}
	return nil
}
<commit_msg>Send messages with the correct byte order<commit_after>package dbus

import (
	"bytes"
	"encoding/binary"
	"io"
	"reflect"
)

const protoVersion byte = 1

// Flags represents the possible flags of a DBus message.
type Flags byte

const (
	NoReplyExpected Flags = 1 << iota
	NoAutoStart
)

// Type represents the possible types of a DBus message.
type Type byte

const (
	TypeMethodCall Type = 1 + iota
	TypeMethodReply
	TypeError
	TypeSignal
	typeMax
)

// HeaderField represents the possible byte codes for the headers
// of a DBus message.
type HeaderField byte

const (
	FieldPath HeaderField = 1 + iota
	FieldInterface
	FieldMember
	FieldErrorName
	FieldReplySerial
	FieldDestination
	FieldSender
	FieldSignature
	FieldUnixFds
	fieldMax
)

// An InvalidMessageError describes the reason why a DBus message is regarded as
// invalid.
type InvalidMessageError string

func (e InvalidMessageError) Error() string {
	return "invalid message: " + string(e)
}

var fieldTypes = map[HeaderField]reflect.Type{
	FieldPath:        objectPathType,
	FieldInterface:   stringType,
	FieldMember:      stringType,
	FieldErrorName:   stringType,
	FieldReplySerial: uint32Type,
	FieldDestination: stringType,
	FieldSender:      stringType,
	FieldSignature:   signatureType,
	FieldUnixFds:     uint32Type,
}

var requiredFields = map[Type][]HeaderField{
	TypeMethodCall:  []HeaderField{FieldPath, FieldMember},
	TypeMethodReply: []HeaderField{FieldReplySerial},
	TypeError:       []HeaderField{FieldErrorName, FieldReplySerial},
	TypeSignal:      []HeaderField{FieldPath, FieldInterface, FieldMember},
}

// Message represents a single DBus message.
type Message struct {
	// must be binary.BigEndian or binary.LittleEndian
	Order binary.ByteOrder

	Type
	Flags
	Serial  uint32
	Headers map[HeaderField]Variant
	Body    []byte
}

type header struct {
	HeaderField
	Variant
}

// DecodeMessage tries to decode a single message from the given reader.
// The byte order is figured out from the first byte. The possibly returned
// error may either be an error of the underlying reader or an
// InvalidMessageError.
func DecodeMessage(rd io.Reader) (message *Message, err error) {
	var order binary.ByteOrder
	var length uint32
	var proto byte
	var headers []header

	b := make([]byte, 1)
	_, err = rd.Read(b)
	if err != nil {
		return
	}
	switch b[0] {
	case 'l':
		order = binary.LittleEndian
	case 'B':
		order = binary.BigEndian
	default:
		return nil, InvalidMessageError("invalid byte order")
	}

	dec := NewDecoder(rd, order)
	dec.pos = 1

	message = new(Message)
	message.Order = order
	err = dec.DecodeMulti(&message.Type, &message.Flags, &proto, &length,
		&message.Serial, &headers)
	if err != nil {
		return nil, err
	}

	message.Headers = make(map[HeaderField]Variant)
	for _, v := range headers {
		message.Headers[v.HeaderField] = v.Variant
	}

	dec.align(8)
	message.Body = make([]byte, int(length))
	if length != 0 {
		_, err := rd.Read(message.Body)
		if err != nil {
			return nil, err
		}
	}

	if err = message.IsValid(); err != nil {
		return nil, err
	}

	return
}

// EncodeTo encodes and sends a message to the given writer. If the message is
// not valid or an error occurs when writing, an error is returned.
func (message *Message) EncodeTo(out io.Writer) error {
	if err := message.IsValid(); err != nil {
		return err
	}
	vs := make([]interface{}, 7)
	switch message.Order {
	case binary.LittleEndian:
		vs[0] = byte('l')
	case binary.BigEndian:
		vs[0] = byte('B')
	}
	vs[1] = message.Type
	vs[2] = message.Flags
	vs[3] = protoVersion
	vs[4] = uint32(len(message.Body))
	vs[5] = message.Serial
	headers := make([]header, 0)
	for k, v := range message.Headers {
		headers = append(headers, header{k, v})
	}
	vs[6] = headers
	buf := new(bytes.Buffer)
	enc := NewEncoder(buf, message.Order)
	enc.EncodeMulti(vs...)
	enc.align(8)
	if len(message.Body) != 0 {
		buf.Write(message.Body)
	}
	if _, err := buf.WriteTo(out); err != nil {
		return err
	}
	return nil
}

// IsValid checks whether message is a valid message and returns an
// InvalidMessageError if it is not.
func (message *Message) IsValid() error {
	switch message.Order {
	case binary.LittleEndian, binary.BigEndian:
	default:
		return InvalidMessageError("invalid byte order")
	}
	if message.Flags & ^(NoAutoStart|NoReplyExpected) != 0 {
		return InvalidMessageError("invalid flags")
	}
	if message.Type == 0 || message.Type >= typeMax {
		return InvalidMessageError("invalid message type")
	}
	for k, v := range message.Headers {
		if k == 0 || k >= fieldMax {
			return InvalidMessageError("invalid header")
		}
		if reflect.TypeOf(v.value) != fieldTypes[k] {
			return InvalidMessageError("invalid type of header field")
		}
	}
	for _, v := range requiredFields[message.Type] {
		if _, ok := message.Headers[v]; !ok {
			return InvalidMessageError("missing required header")
		}
	}
	if path, ok := message.Headers[FieldPath]; ok {
		if !path.value.(ObjectPath).IsValid() {
			return InvalidMessageError("invalid path")
		}
	}
	if len(message.Body) != 0 {
		if _, ok := message.Headers[FieldSignature]; !ok {
			return InvalidMessageError("missing signature")
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package softlayer

import (
	"errors"
	"fmt"
	"strconv"
	"testing"

	"strings"

	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"
	"github.ibm.com/riethm/gopherlayer.git/datatypes"
	"github.ibm.com/riethm/gopherlayer.git/services"
	"github.ibm.com/riethm/gopherlayer.git/session"
)

func TestAccSoftLayerScaleGroup_Basic(t *testing.T) {
	var scalegroup datatypes.Scale_Group

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckSoftLayerScaleGroupDestroy,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccCheckSoftLayerScaleGroupConfig_basic,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckSoftLayerScaleGroupExists("softlayer_scale_group.sample-http-cluster", &scalegroup),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "name", "sample-http-cluster"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "regional_group", "as-sgp-central-1"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "cooldown", "30"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "minimum_member_count", "1"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "maximum_member_count", "10"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "termination_policy", "CLOSEST_TO_NEXT_CHARGE"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_server_id", "267513"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "port", "8080"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "health_check.type", "HTTP"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.name", "test-VM"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.domain", "example.com"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.cpu", "1"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.ram", "4096"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.public_network_speed", "1000"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.hourly_billing", "true"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.image", "DEBIAN_7_64"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.local_disk", "false"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.disks.0", "25"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.disks.1", "100"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.datacenter", "sng01"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.post_install_script_uri", ""),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.ssh_keys.0", "383111"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.user_data", "#!/bin/bash"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "network_vlans.#", "1"),
					testAccCheckSoftLayerScaleGroupContainsNetworkVlan(&scalegroup, 1928, "bcr02a.sng01"),
				),
			},

			resource.TestStep{
				Config: testAccCheckSoftLayerScaleGroupConfig_updated,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckSoftLayerScaleGroupExists("softlayer_scale_group.sample-http-cluster", &scalegroup),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "name", "changed_name"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "regional_group", "as-sgp-central-1"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "minimum_member_count", "2"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "maximum_member_count", "12"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "termination_policy", "NEWEST"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "cooldown", "35"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "port", "9090"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "health_check.type", "HTTP-CUSTOM"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.name", "example-VM"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.domain", "test.com"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.cpu", "2"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.ram", "8192"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.public_network_speed", "100"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.image", "CENTOS_7_64"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.datacenter", "sng01"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.post_install_script_uri", "http://localhost/index.html"),
				),
			},
		},
	})
}

func testAccCheckSoftLayerScaleGroupDestroy(s *terraform.State) error {
	service := services.GetScaleGroupService(testAccProvider.Meta().(*session.Session))

	for _, rs := range s.RootModule().Resources {
		if rs.Type != "softlayer_scale_group" {
			continue
		}

		scalegroupId, _ := strconv.Atoi(rs.Primary.ID)

		// Try to find the key
		_, err := service.Id(scalegroupId).Mask("id").GetObject()

		if err != nil && !strings.Contains(err.Error(), "404") {
			return fmt.Errorf("Error waiting for Auto Scale (%s) to be destroyed: %s", rs.Primary.ID, err)
		}
	}

	return nil
}

func testAccCheckSoftLayerScaleGroupContainsNetworkVlan(scaleGroup *datatypes.Scale_Group, vlanNumber int, primaryRouterHostname string) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		found := false

		for _, scaleVlan := range scaleGroup.NetworkVlans {
			vlan := *scaleVlan.NetworkVlan

			if *vlan.VlanNumber == vlanNumber && *vlan.PrimaryRouter.Hostname == primaryRouterHostname {
				found = true
				break
			}
		}

		if !found {
			return fmt.Errorf(
				"Vlan number %d with router hostname %s not found in scale group",
				vlanNumber,
				primaryRouterHostname)
		}

		return nil
	}
}

func testAccCheckSoftLayerScaleGroupExists(n string, scalegroup *datatypes.Scale_Group) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[n]

		if !ok {
			return fmt.Errorf("Not found: %s", n)
		}

		if rs.Primary.ID == "" {
			return errors.New("No Record ID is set")
		}

		scalegroupId, _ := strconv.Atoi(rs.Primary.ID)

		service := services.GetScaleGroupService(testAccProvider.Meta().(*session.Session))
		foundScaleGroup, err := service.Id(scalegroupId).Mask(strings.Join(SoftLayerScaleGroupObjectMask, ";")).GetObject()

		if err != nil {
			return err
		}

		if strconv.Itoa(int(*foundScaleGroup.Id)) != rs.Primary.ID {
			return fmt.Errorf("Record %s not found", rs.Primary.ID)
		}

		*scalegroup = foundScaleGroup

		return nil
	}
}

const testAccCheckSoftLayerScaleGroupConfig_basic = `
resource "softlayer_scale_group" "sample-http-cluster" {
    name = "sample-http-cluster"
    regional_group = "as-sgp-central-1"
    cooldown = 30
    minimum_member_count = 1
    maximum_member_count = 10
    termination_policy = "CLOSEST_TO_NEXT_CHARGE"
    virtual_server_id = 267513
    port = 8080
    health_check = {
        type = "HTTP"
    }
    virtual_guest_member_template = {
        name = "test-VM"
        domain = "example.com"
        cpu = 1
        ram = 4096
        public_network_speed = 1000
        hourly_billing = true
        image = "DEBIAN_7_64"
        local_disk = false
        disks = [25,100]
        datacenter = "sng01"
        post_install_script_uri = ""
        ssh_keys = [383111]
        user_data = "#!/bin/bash"
    }
    network_vlans = {
            vlan_number = "1928"
            primary_router_hostname = "bcr02a.sng01"
    }

}`

const testAccCheckSoftLayerScaleGroupConfig_updated = `
resource "softlayer_scale_group" "sample-http-cluster" {
    name = "changed_name"
    regional_group = "as-sgp-central-1"
    cooldown = 35
    minimum_member_count = 2
    maximum_member_count = 12
    termination_policy = "NEWEST"
    virtual_server_id = 267513
    port = 9090
    health_check = {
        type = "HTTP-CUSTOM"
        custom_method = "GET"
        custom_request = "/healthcheck"
        custom_response = 200
    }
    virtual_guest_member_template = {
        name = "example-VM"
        domain = "test.com"
        cpu = 2
        ram = 8192
        public_network_speed = 100
        hourly_billing = true
        image = "CENTOS_7_64"
        local_disk = false
        disks = [25,100]
        datacenter = "sng01"
        post_install_script_uri = "http://localhost/index.html"
        ssh_keys = [383111]
        user_data = "#!/bin/bash"
    }
    network_vlans = {
        vlan_number = "1928"
        primary_router_hostname = "bcr02a.sng01"
    }
}`
<commit_msg>Update acceptance test to remove dependency on pre-existing load balancer<commit_after>package softlayer

import (
	"errors"
	"fmt"
	"strconv"
	"testing"

	"strings"

	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"
	"github.ibm.com/riethm/gopherlayer.git/datatypes"
	"github.ibm.com/riethm/gopherlayer.git/services"
	"github.ibm.com/riethm/gopherlayer.git/session"
)

func TestAccSoftLayerScaleGroup_Basic(t *testing.T) {
	var scalegroup datatypes.Scale_Group

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckSoftLayerScaleGroupDestroy,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccCheckSoftLayerScaleGroupConfig_basic,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckSoftLayerScaleGroupExists("softlayer_scale_group.sample-http-cluster", &scalegroup),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "name", "sample-http-cluster"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "regional_group", "as-sgp-central-1"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "cooldown", "30"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "minimum_member_count", "1"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "maximum_member_count", "10"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "termination_policy", "CLOSEST_TO_NEXT_CHARGE"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "port", "8080"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "health_check.type", "HTTP"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.name", "test-VM"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.domain", "example.com"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.cpu", "1"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.ram", "4096"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.public_network_speed", "1000"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.hourly_billing", "true"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.image", "DEBIAN_7_64"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.local_disk", "false"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.disks.0", "25"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.disks.1", "100"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.datacenter", "sng01"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.post_install_script_uri", ""),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.ssh_keys.0", "383111"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.user_data", "#!/bin/bash"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "network_vlans.#", "1"),
					testAccCheckSoftLayerScaleGroupContainsNetworkVlan(&scalegroup, 1928, "bcr02a.sng01"),
				),
			},

			resource.TestStep{
				Config: testAccCheckSoftLayerScaleGroupConfig_updated,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckSoftLayerScaleGroupExists("softlayer_scale_group.sample-http-cluster", &scalegroup),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "name", "changed_name"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "regional_group", "as-sgp-central-1"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "minimum_member_count", "2"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "maximum_member_count", "12"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "termination_policy", "NEWEST"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "cooldown", "35"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "port", "9090"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "health_check.type", "HTTP-CUSTOM"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.name", "example-VM"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.domain", "test.com"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.cpu", "2"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.ram", "8192"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.public_network_speed", "100"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.image", "CENTOS_7_64"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.datacenter", "sng01"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.post_install_script_uri", "http://localhost/index.html"),
				),
			},
		},
	})
}

func testAccCheckSoftLayerScaleGroupDestroy(s *terraform.State) error {
	service := services.GetScaleGroupService(testAccProvider.Meta().(*session.Session))

	for _, rs := range s.RootModule().Resources {
		if rs.Type != "softlayer_scale_group" {
			continue
		}

		scalegroupId, _ := strconv.Atoi(rs.Primary.ID)

		// Try to find the key
		_, err := service.Id(scalegroupId).Mask("id").GetObject()

		if err != nil && !strings.Contains(err.Error(), "404") {
			return fmt.Errorf("Error waiting for Auto Scale (%s) to be destroyed: %s", rs.Primary.ID, err)
		}
	}

	return nil
}

func testAccCheckSoftLayerScaleGroupContainsNetworkVlan(scaleGroup *datatypes.Scale_Group, vlanNumber int, primaryRouterHostname string) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		found := false

		for _, scaleVlan := range scaleGroup.NetworkVlans {
			vlan := *scaleVlan.NetworkVlan

			if *vlan.VlanNumber == vlanNumber && *vlan.PrimaryRouter.Hostname == primaryRouterHostname {
				found = true
				break
			}
		}

		if !found {
			return fmt.Errorf(
				"Vlan number %d with router hostname %s not found in scale group",
				vlanNumber,
				primaryRouterHostname)
		}

		return nil
	}
}

func testAccCheckSoftLayerScaleGroupExists(n string, scalegroup *datatypes.Scale_Group) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[n]

		if !ok {
			return fmt.Errorf("Not found: %s", n)
		}

		if rs.Primary.ID == "" {
			return errors.New("No Record ID is set")
		}

		scalegroupId, _ := strconv.Atoi(rs.Primary.ID)

		service := services.GetScaleGroupService(testAccProvider.Meta().(*session.Session))
		foundScaleGroup, err := service.Id(scalegroupId).Mask(strings.Join(SoftLayerScaleGroupObjectMask, ",")).GetObject()

		if err != nil {
			return err
		}

		if strconv.Itoa(int(*foundScaleGroup.Id)) != rs.Primary.ID {
			return fmt.Errorf("Record %s not found", rs.Primary.ID)
		}

		*scalegroup = foundScaleGroup

		return nil
	}
}

const testAccCheckSoftLayerScaleGroupConfig_basic = `
resource "softlayer_lb_local" "local_lb_01" {
    connections = 15000
    datacenter = "sng01"
    ha_enabled = false
}

resource "softlayer_lb_local_service_group" "http_sg" {
    load_balancer_id = "${softlayer_lb_local.local_lb_01.id}"
    allocation = 100
    port = 80
    routing_method = "ROUND_ROBIN"
    routing_type = "HTTP"
}

resource "softlayer_scale_group" "sample-http-cluster" {
    name = "sample-http-cluster"
    regional_group = "as-sgp-central-1"
    cooldown = 30
    minimum_member_count = 1
    maximum_member_count = 10
    termination_policy = "CLOSEST_TO_NEXT_CHARGE"
    virtual_server_id = "${softlayer_lb_local_service_group.http_sg.id}"
    port = 8080
    health_check = {
        type = "HTTP"
    }
    virtual_guest_member_template = {
        name = "test-VM"
        domain = "example.com"
        cpu = 1
        ram = 4096
        public_network_speed = 1000
        hourly_billing = true
        image = "DEBIAN_7_64"
        local_disk = false
        disks = [25,100]
        datacenter = "sng01"
        post_install_script_uri = ""
        ssh_keys = [383111]
        user_data = "#!/bin/bash"
    }
    network_vlans = {
            vlan_number = "1928"
            primary_router_hostname = "bcr02a.sng01"
    }

}`

const testAccCheckSoftLayerScaleGroupConfig_updated = `
resource "softlayer_lb_local" "local_lb_01" {
    connections = 15000
    datacenter = "sng01"
    ha_enabled = false
}

resource "softlayer_lb_local_service_group" "http_sg" {
    load_balancer_id = "${softlayer_lb_local.local_lb_01.id}"
    allocation = 100
    port = 80
    routing_method = "ROUND_ROBIN"
    routing_type = "HTTP"
}

resource "softlayer_scale_group" "sample-http-cluster" {
    name = "changed_name"
    regional_group = "as-sgp-central-1"
    cooldown = 35
    minimum_member_count = 2
    maximum_member_count = 12
    termination_policy = "NEWEST"
    virtual_server_id = "${softlayer_lb_local_service_group.http_sg.id}"
    port = 9090
    health_check = {
        type = "HTTP-CUSTOM"
        custom_method = "GET"
        custom_request = "/healthcheck"
        custom_response = 200
    }
    virtual_guest_member_template = {
        name = "example-VM"
        domain = "test.com"
        cpu = 2
        ram = 8192
        public_network_speed = 100
        hourly_billing = true
        image = "CENTOS_7_64"
        local_disk = false
        disks = [25,100]
        datacenter = "sng01"
        post_install_script_uri = "http://localhost/index.html"
        ssh_keys = [383111]
        user_data = "#!/bin/bash"
    }
    network_vlans = {
        vlan_number = "1928"
        primary_router_hostname = "bcr02a.sng01"
    }
}`
<|endoftext|>


<commit_before>package gateway

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"errors"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"reflect"
	"strconv"
	"strings"
	"time"

	"github.com/robertkrimen/otto"
	_ "github.com/robertkrimen/otto/underscore"

	"github.com/TykTechnologies/tyk/apidef"
	"github.com/TykTechnologies/tyk/config"
	"github.com/TykTechnologies/tyk/headers"
	"github.com/TykTechnologies/tyk/user"

	"github.com/sirupsen/logrus"
)

// RequestObject is marshalled to JSON string and passed into JSON middleware
type RequestObject struct {
	Headers map[string][]string
	Body    string
	URL     string
	Params  map[string][]string
	Scheme  string
}

type ResponseObject struct {
	Body    string
	Headers map[string]string
	Code    int
}

type VMResponseObject struct {
	Response    ResponseObject
	SessionMeta map[string]interface{}
}

// DynamicMiddleware is a generic middleware that will execute JS code before continuing
type VirtualEndpoint struct {
	BaseMiddleware
	sh SuccessHandler
}

func (d *VirtualEndpoint) Name() string {
	return "VirtualEndpoint"
}

func preLoadVirtualMetaCode(meta *apidef.VirtualMeta, j *JSVM) {
	// the only call site uses (&foo, &bar) so meta and j won't be
	// nil.
	var src interface{}
	switch meta.FunctionSourceType {
	case "file":
		j.Log.Debug("Loading JS Endpoint File: ", meta.FunctionSourceURI)
		f, err := os.Open(meta.FunctionSourceURI)
		if err != nil {
			j.Log.WithError(err).Error("Failed to open Endpoint JS")
			return
		}
		src = f
	case "blob":
		if config.Global().DisableVirtualPathBlobs {
			j.Log.Error("[JSVM] Blobs not allowed on this node")
			return
		}
		j.Log.Debug("Loading JS blob")
		js, err := base64.StdEncoding.DecodeString(meta.FunctionSourceURI)
		if err != nil {
			j.Log.WithError(err).Error("Failed to load blob JS")
			return
		}
		src = js
	default:
		j.Log.Error("Type must be either file or blob (base64)!")
		return
	}
	if _, err := j.VM.Run(src); err != nil {
		j.Log.WithError(err).Error("Could not load virtual endpoint JS")
	}
}

func (d *VirtualEndpoint) Init() {
	d.sh = SuccessHandler{d.BaseMiddleware}
}

func (d *VirtualEndpoint) EnabledForSpec() bool {
	if !d.Spec.GlobalConfig.EnableJSVM {
		return false
	}
	for _, version := range d.Spec.VersionData.Versions {
		if len(version.ExtendedPaths.Virtual) > 0 {
			return true
		}
	}
	return false
}

func (d *VirtualEndpoint) getMetaFromRequest(r *http.Request) *apidef.VirtualMeta {
	_, versionPaths, _, _ := d.Spec.Version(r)
	found, meta := d.Spec.CheckSpecMatchesStatus(r, versionPaths, VirtualPath)
	if !found {
		return nil
	}

	vmeta, ok := meta.(*apidef.VirtualMeta)
	if !ok {
		return nil
	}

	return vmeta
}

func (d *VirtualEndpoint) ServeHTTPForCache(w http.ResponseWriter, r *http.Request, vmeta *apidef.VirtualMeta) *http.Response {
	t1 := time.Now().UnixNano()

	if vmeta == nil {
		if vmeta = d.getMetaFromRequest(r); vmeta == nil {
			return nil
		}
	}

	// Create the proxy object
	originalBody, err := ioutil.ReadAll(r.Body)
	if err != nil {
		d.Logger().WithError(err).Error("Failed to read request body!")
		return nil
	}
	defer r.Body.Close()

	scheme := "http"
	if r.TLS != nil {
		scheme = "https"
	}
	requestData := RequestObject{
		Headers: r.Header,
		Body:    string(originalBody),
		URL:     r.URL.String(),
		Scheme:  scheme,
	}

	// We need to copy the body _back_ for the decode
	r.Body = ioutil.NopCloser(bytes.NewReader(originalBody))
	parseForm(r)
	requestData.Params = r.Form

	requestAsJson, err := json.Marshal(requestData)
	if err != nil {
		d.Logger().WithError(err).Error("Failed to encode request object for virtual endpoint")
		return nil
	}

	// Encode the configuration data too
	specAsJson := specToJson(d.Spec)

	session := user.NewSessionState()

	// Encode the session object (if not a pre-process)
	if vmeta.UseSession {
		session = ctxGetSession(r)
	}

	sessionAsJson, err := json.Marshal(session)
	if err != nil {
		d.Logger().WithError(err).Error("Failed to encode session for VM")
		return nil
	}

	// Run the middleware
	vm := d.Spec.JSVM.VM.Copy()
	vm.Interrupt = make(chan func(), 1)
	d.Logger().Debug("Running: ", vmeta.ResponseFunctionName)
	// buffered, leaving no chance of a goroutine leak since the
	// spawned goroutine will send 0 or 1 values.
	ret := make(chan otto.Value, 1)
	errRet := make(chan error, 1)
	go func() {
		defer func() {
			// the VM executes the panic func that gets it
			// to stop, so we must recover here to not crash
			// the whole Go program.
			recover()
		}()
		returnRaw, err := vm.Run(vmeta.ResponseFunctionName + `(` + string(requestAsJson) + `, ` + string(sessionAsJson) + `, ` + specAsJson + `);`)
		ret <- returnRaw
		errRet <- err
	}()
	var returnRaw otto.Value
	t := time.NewTimer(d.Spec.JSVM.Timeout)
	select {
	case returnRaw = <-ret:
		if err := <-errRet; err != nil {
			d.Logger().WithError(err).Error("Failed to run JS middleware")
			return nil
		}
		t.Stop()
	case <-t.C:
		t.Stop()
		d.Logger().Error("JS middleware timed out after ", d.Spec.JSVM.Timeout)
		vm.Interrupt <- func() {
			// only way to stop the VM is to send it a func
			// that panics.
			panic("stop")
		}
		return nil
	}
	returnDataStr, _ := returnRaw.ToString()

	// Decode the return object
	newResponseData := VMResponseObject{}
	if err := json.Unmarshal([]byte(returnDataStr), &newResponseData); err != nil {
		d.Logger().WithError(err).Error("Failed to decode virtual endpoint response data on return from VM: ",
			"; Returned: ", returnDataStr)
		return nil
	}

	// Save the sesison data (if modified)
	if vmeta.UseSession {
		newMeta := newResponseData.SessionMeta
		if !reflect.DeepEqual(session.GetMetaData(), newMeta) {
			session.SetMetaData(newMeta)
			ctxSetSession(r, session, "", true)
		}
	}

	d.Logger().Debug("JSVM Virtual Endpoint execution took: (ns) ", time.Now().UnixNano()-t1)

	copiedResponse := forceResponse(w, r, &newResponseData, d.Spec, session, false, d.Logger())

	if copiedResponse != nil {
		d.sh.RecordHit(r, Latency{}, copiedResponse.StatusCode, copiedResponse)
	}

	return copiedResponse
}

func forceResponse(w http.ResponseWriter,
	r *http.Request,
	newResponseData *VMResponseObject,
	spec *APISpec,
	session *user.SessionState, isPre bool, logger *logrus.Entry) *http.Response {
	responseMessage := []byte(newResponseData.Response.Body)

	// Create an http.Response object so we can send it tot he cache middleware
	newResponse := new(http.Response)
	newResponse.Header = make(map[string][]string)

	requestTime := time.Now().UTC().Format(http.TimeFormat)
	ignoreCanonical := config.Global().IgnoreCanonicalMIMEHeaderKey
	for header, value := range newResponseData.Response.Headers {
		setCustomHeader(newResponse.Header, header, value, ignoreCanonical)
	}

	newResponse.ContentLength = int64(len(responseMessage))
	newResponse.Body = nopCloser{
		ReadSeeker: bytes.NewReader(responseMessage),
	}
	newResponse.StatusCode = newResponseData.Response.Code
	newResponse.Proto = "HTTP/1.0"
	newResponse.ProtoMajor = 1
	newResponse.ProtoMinor = 0
	newResponse.Header.Set("Server", "tyk")
	newResponse.Header.Set("Date", requestTime)

	// Check if it is a loop
	loc := newResponse.Header.Get("Location")
	if (newResponse.StatusCode == 301 || newResponse.StatusCode == 302) && strings.HasPrefix(loc, "tyk://") {
		loopURL, err := url.Parse(newResponse.Header.Get("Location"))
		if err != nil {
			logger.WithError(err).WithField("loop", loc).Error("Failed to parse loop url")
		} else {
			ctxSetOrigRequestURL(r, r.URL)
			r.URL = loopURL
		}

		return nil
	}

	if !isPre {
		// Handle response middleware
		if _, err := handleResponseChain(spec.ResponseChain, w, newResponse, r, session); err != nil {
			logger.WithError(err).Error("Response chain failed! ")
		}
	}

	handleForcedResponse(w, newResponse, session, spec)

	// Record analytics
	return newResponse
}

// ProcessRequest will run any checks on the request on the way through the system, return an error to have the chain fail
func (d *VirtualEndpoint) ProcessRequest(w http.ResponseWriter, r *http.Request, _ interface{}) (error, int) {
	vmeta := d.getMetaFromRequest(r)
	if vmeta == nil {
		// nothing can be done here, reply with 200 to allow proxy to target
		return nil, http.StatusOK
	}

	if res := d.ServeHTTPForCache(w, r, vmeta); res == nil {
		if vmeta.ProxyOnError {
			return nil, http.StatusOK
		} else {
			return errors.New("Error during virtual endpoint execution. Contact Administrator for more details."), http.StatusInternalServerError
		}
	}

	return nil, mwStatusRespond
}

func (d *VirtualEndpoint) HandleResponse(rw http.ResponseWriter, res *http.Response, ses *user.SessionState) {
	// Externalising this from the MW so we can re-use it elsewhere
	handleForcedResponse(rw, res, ses, d.Spec)
}

func handleForcedResponse(rw http.ResponseWriter, res *http.Response, ses *user.SessionState, spec *APISpec) {
	defer res.Body.Close()

	// Close connections
	if spec.GlobalConfig.CloseConnections {
		res.Header.Set("Connection", "close")
	}

	// Add resource headers
	if ses != nil {
		// We have found a session, lets report back
		quotaMax, quotaRemaining, _, quotaRenews := ses.GetQuotaLimitByAPIID(spec.APIID)
		res.Header.Set(headers.XRateLimitLimit, strconv.Itoa(int(quotaMax)))
		res.Header.Set(headers.XRateLimitRemaining, strconv.Itoa(int(quotaRemaining)))
		res.Header.Set(headers.XRateLimitReset, strconv.Itoa(int(quotaRenews)))
	}

	copyHeader(rw.Header(), res.Header, config.Global().IgnoreCanonicalMIMEHeaderKey)

	rw.WriteHeader(res.StatusCode)
	io.Copy(rw, res.Body)
}
<commit_msg>mw_virtual_endpoint: track latency (#2931) (#3386)<commit_after>package gateway

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"errors"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"reflect"
	"strconv"
	"strings"
	"time"

	"github.com/robertkrimen/otto"
	_ "github.com/robertkrimen/otto/underscore"

	"github.com/TykTechnologies/tyk/apidef"
	"github.com/TykTechnologies/tyk/config"
	"github.com/TykTechnologies/tyk/headers"
	"github.com/TykTechnologies/tyk/user"

	"github.com/sirupsen/logrus"
)

// RequestObject is marshalled to JSON string and passed into JSON middleware
type RequestObject struct {
	Headers map[string][]string
	Body    string
	URL     string
	Params  map[string][]string
	Scheme  string
}

type ResponseObject struct {
	Body    string
	Headers map[string]string
	Code    int
}

type VMResponseObject struct {
	Response    ResponseObject
	SessionMeta map[string]interface{}
}

// DynamicMiddleware is a generic middleware that will execute JS code before continuing
type VirtualEndpoint struct {
	BaseMiddleware
	sh SuccessHandler
}

func (d *VirtualEndpoint) Name() string {
	return "VirtualEndpoint"
}

func preLoadVirtualMetaCode(meta *apidef.VirtualMeta, j *JSVM) {
	// the only call site uses (&foo, &bar) so meta and j won't be
	// nil.
	var src interface{}
	switch meta.FunctionSourceType {
	case "file":
		j.Log.Debug("Loading JS Endpoint File: ", meta.FunctionSourceURI)
		f, err := os.Open(meta.FunctionSourceURI)
		if err != nil {
			j.Log.WithError(err).Error("Failed to open Endpoint JS")
			return
		}
		src = f
	case "blob":
		if config.Global().DisableVirtualPathBlobs {
			j.Log.Error("[JSVM] Blobs not allowed on this node")
			return
		}
		j.Log.Debug("Loading JS blob")
		js, err := base64.StdEncoding.DecodeString(meta.FunctionSourceURI)
		if err != nil {
			j.Log.WithError(err).Error("Failed to load blob JS")
			return
		}
		src = js
	default:
		j.Log.Error("Type must be either file or blob (base64)!")
		return
	}
	if _, err := j.VM.Run(src); err != nil {
		j.Log.WithError(err).Error("Could not load virtual endpoint JS")
	}
}

func (d *VirtualEndpoint) Init() {
	d.sh = SuccessHandler{d.BaseMiddleware}
}

func (d *VirtualEndpoint) EnabledForSpec() bool {
	if !d.Spec.GlobalConfig.EnableJSVM {
		return false
	}
	for _, version := range d.Spec.VersionData.Versions {
		if len(version.ExtendedPaths.Virtual) > 0 {
			return true
		}
	}
	return false
}

func (d *VirtualEndpoint) getMetaFromRequest(r *http.Request) *apidef.VirtualMeta {
	_, versionPaths, _, _ := d.Spec.Version(r)
	found, meta := d.Spec.CheckSpecMatchesStatus(r, versionPaths, VirtualPath)
	if !found {
		return nil
	}

	vmeta, ok := meta.(*apidef.VirtualMeta)
	if !ok {
		return nil
	}

	return vmeta
}

func (d *VirtualEndpoint) ServeHTTPForCache(w http.ResponseWriter, r *http.Request, vmeta *apidef.VirtualMeta) *http.Response {
	t1 := time.Now()
	if vmeta == nil {
		if vmeta = d.getMetaFromRequest(r); vmeta == nil {
			return nil
		}
	}

	// Create the proxy object
	originalBody, err := ioutil.ReadAll(r.Body)
	if err != nil {
		d.Logger().WithError(err).Error("Failed to read request body!")
		return nil
	}
	defer r.Body.Close()

	scheme := "http"
	if r.TLS != nil {
		scheme = "https"
	}
	requestData := RequestObject{
		Headers: r.Header,
		Body:    string(originalBody),
		URL:     r.URL.String(),
		Scheme:  scheme,
	}

	// We need to copy the body _back_ for the decode
	r.Body = ioutil.NopCloser(bytes.NewReader(originalBody))
	parseForm(r)
	requestData.Params = r.Form

	requestAsJson, err := json.Marshal(requestData)
	if err != nil {
		d.Logger().WithError(err).Error("Failed to encode request object for virtual endpoint")
		return nil
	}

	// Encode the configuration data too
	specAsJson := specToJson(d.Spec)

	session := user.NewSessionState()

	// Encode the session object (if not a pre-process)
	if vmeta.UseSession {
		session = ctxGetSession(r)
	}

	sessionAsJson, err := json.Marshal(session)
	if err != nil {
		d.Logger().WithError(err).Error("Failed to encode session for VM")
		return nil
	}

	// Run the middleware
	vm := d.Spec.JSVM.VM.Copy()
	vm.Interrupt = make(chan func(), 1)
	d.Logger().Debug("Running: ", vmeta.ResponseFunctionName)
	// buffered, leaving no chance of a goroutine leak since the
	// spawned goroutine will send 0 or 1 values.
	ret := make(chan otto.Value, 1)
	errRet := make(chan error, 1)
	go func() {
		defer func() {
			// the VM executes the panic func that gets it
			// to stop, so we must recover here to not crash
			// the whole Go program.
			recover()
		}()
		returnRaw, err := vm.Run(vmeta.ResponseFunctionName + `(` + string(requestAsJson) + `, ` + string(sessionAsJson) + `, ` + specAsJson + `);`)
		ret <- returnRaw
		errRet <- err
	}()
	var returnRaw otto.Value
	t := time.NewTimer(d.Spec.JSVM.Timeout)
	select {
	case returnRaw = <-ret:
		if err := <-errRet; err != nil {
			d.Logger().WithError(err).Error("Failed to run JS middleware")
			return nil
		}
		t.Stop()
	case <-t.C:
		t.Stop()
		d.Logger().Error("JS middleware timed out after ", d.Spec.JSVM.Timeout)
		vm.Interrupt <- func() {
			// only way to stop the VM is to send it a func
			// that panics.
			panic("stop")
		}
		return nil
	}
	returnDataStr, _ := returnRaw.ToString()

	// Decode the return object
	newResponseData := VMResponseObject{}
	if err := json.Unmarshal([]byte(returnDataStr), &newResponseData); err != nil {
		d.Logger().WithError(err).Error("Failed to decode virtual endpoint response data on return from VM: ",
			"; Returned: ", returnDataStr)
		return nil
	}

	// Save the sesison data (if modified)
	if vmeta.UseSession {
		newMeta := newResponseData.SessionMeta
		if !reflect.DeepEqual(session.GetMetaData(), newMeta) {
			session.SetMetaData(newMeta)
			ctxSetSession(r, session, "", true)
		}
	}

	copiedResponse := forceResponse(w, r, &newResponseData, d.Spec, session, false, d.Logger())
	ms := DurationToMillisecond(time.Since(t1))
	d.Logger().Debug("JSVM Virtual Endpoint execution took: (ms) ", ms)

	if copiedResponse != nil {
		d.sh.RecordHit(r, Latency{Total: int64(ms)}, copiedResponse.StatusCode, copiedResponse)
	}

	return copiedResponse
}

func forceResponse(w http.ResponseWriter,
	r *http.Request,
	newResponseData *VMResponseObject,
	spec *APISpec,
	session *user.SessionState, isPre bool, logger *logrus.Entry) *http.Response {
	responseMessage := []byte(newResponseData.Response.Body)

	// Create an http.Response object so we can send it tot he cache middleware
	newResponse := new(http.Response)
	newResponse.Header = make(map[string][]string)

	requestTime := time.Now().UTC().Format(http.TimeFormat)
	ignoreCanonical := config.Global().IgnoreCanonicalMIMEHeaderKey
	for header, value := range newResponseData.Response.Headers {
		setCustomHeader(newResponse.Header, header, value, ignoreCanonical)
	}

	newResponse.ContentLength = int64(len(responseMessage))
	newResponse.Body = nopCloser{
		ReadSeeker: bytes.NewReader(responseMessage),
	}
	newResponse.StatusCode = newResponseData.Response.Code
	newResponse.Proto = "HTTP/1.0"
	newResponse.ProtoMajor = 1
	newResponse.ProtoMinor = 0
	newResponse.Header.Set("Server", "tyk")
	newResponse.Header.Set("Date", requestTime)

	// Check if it is a loop
	loc := newResponse.Header.Get("Location")
	if (newResponse.StatusCode == 301 || newResponse.StatusCode == 302) && strings.HasPrefix(loc, "tyk://") {
		loopURL, err := url.Parse(newResponse.Header.Get("Location"))
		if err != nil {
			logger.WithError(err).WithField("loop", loc).Error("Failed to parse loop url")
		} else {
			ctxSetOrigRequestURL(r, r.URL)
			r.URL = loopURL
		}

		return nil
	}

	if !isPre {
		// Handle response middleware
		if _, err := handleResponseChain(spec.ResponseChain, w, newResponse, r, session); err != nil {
			logger.WithError(err).Error("Response chain failed! ")
		}
	}

	handleForcedResponse(w, newResponse, session, spec)

	// Record analytics
	return newResponse
}

// ProcessRequest will run any checks on the request on the way through the system, return an error to have the chain fail
func (d *VirtualEndpoint) ProcessRequest(w http.ResponseWriter, r *http.Request, _ interface{}) (error, int) {
	vmeta := d.getMetaFromRequest(r)
	if vmeta == nil {
		// nothing can be done here, reply with 200 to allow proxy to target
		return nil, http.StatusOK
	}

	if res := d.ServeHTTPForCache(w, r, vmeta); res == nil {
		if vmeta.ProxyOnError {
			return nil, http.StatusOK
		} else {
			return errors.New("Error during virtual endpoint execution. Contact Administrator for more details."), http.StatusInternalServerError
		}
	}

	return nil, mwStatusRespond
}

func (d *VirtualEndpoint) HandleResponse(rw http.ResponseWriter, res *http.Response, ses *user.SessionState) {
	// Externalising this from the MW so we can re-use it elsewhere
	handleForcedResponse(rw, res, ses, d.Spec)
}

func handleForcedResponse(rw http.ResponseWriter, res *http.Response, ses *user.SessionState, spec *APISpec) {
	defer res.Body.Close()

	// Close connections
	if spec.GlobalConfig.CloseConnections {
		res.Header.Set("Connection", "close")
	}

	// Add resource headers
	if ses != nil {
		// We have found a session, lets report back
		quotaMax, quotaRemaining, _, quotaRenews := ses.GetQuotaLimitByAPIID(spec.APIID)
		res.Header.Set(headers.XRateLimitLimit, strconv.Itoa(int(quotaMax)))
		res.Header.Set(headers.XRateLimitRemaining, strconv.Itoa(int(quotaRemaining)))
		res.Header.Set(headers.XRateLimitReset, strconv.Itoa(int(quotaRenews)))
	}

	copyHeader(rw.Header(), res.Header, config.Global().IgnoreCanonicalMIMEHeaderKey)

	rw.WriteHeader(res.StatusCode)
	io.Copy(rw, res.Body)
}
<|endoftext|>


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

package dlshared

import "time"
import "fmt"

type MetricsRelayFunction func(string, []Metric)

type metricType int8

const (
	Counter metricType = 0
	Gauge metricType = 1

	CounterStr = "counter"
	GaugeStr = "gauge"
)

type Metric struct {
	Name string
	Type metricType
	Value float64
}

type Metrics struct {
	sourceName string
	quitChannel chan bool
	relayFunc MetricsRelayFunction
	relayPeriodInSecs int
	metricChannel chan *Metric
	ticker *time.Ticker
}

// The relay function is only called if there are metrics to relay.
func NewMetrics(	sourceName string,
					relayFunc MetricsRelayFunction,
					relayPeriodInSecs int,
					metricQueueLength int) *Metrics {

	return &Metrics{
		sourceName : sourceName,
		relayFunc : relayFunc,
		relayPeriodInSecs: relayPeriodInSecs,
		quitChannel : make(chan bool),
		metricChannel : make(chan *Metric, metricQueueLength),
	}
}

// Update the gauge value
func (self *Metrics) Gauge(metricName string, value float64) {
	self.metricChannel <- &Metric{
		Name: metricName,
		Type: Gauge,
		Value: value,
	}
}

// Increases the counter by one.
func (self *Metrics) Count(metricName string) {
	self.metricChannel <- &Metric{
		Name: metricName,
		Type: Counter,
		Value: 1,
	}
}

// Increase the counter
func (self *Metrics) CountWithValue(metricName string, value float64) {
	self.metricChannel <- &Metric{
		Name: metricName,
		Type: Counter,
		Value: value,
	}
}

func (self *Metrics) listenForEvents() {

	metrics := make(map[string]Metric)

    for {
        select {
			case metric := <- self.metricChannel:
				current, found := metrics[metric.Name]
				if !found {
					metrics[metric.Name] = *metric
					continue
				}

				if metric.Type == Counter {
					current.Value = current.Value + metric.Value
				} else {
					// This is a gauge
					current.Value = metric.Value
				}

			case <- self.ticker.C:

				fmt.Println("Ready to relay events:", len(metrics))

				counter := 0

				var toRelay []Metric
				fmt.Println("Slice size before setup:", len(toRelay))

				for _, v := range metrics {
					if v.Name == "" {
						fmt.Println("WTF - Emtpy name in to relay setup")
					}
					toRelay = append(toRelay, Metric{ Name: v.Name, Type: v.Type, Value: v.Value })
					counter++
				}

				fmt.Println("Slice size after setup:", len(toRelay))
				fmt.Println("Counter:", counter)

				if len(toRelay) == 0 {
					continue
				}

				go self.relayFunc(self.sourceName, toRelay)

			case <- self.quitChannel:
				self.ticker.Stop()
				return
        }
    }
}

func (self *Metrics) Start() error {

	self.ticker = time.NewTicker(time.Duration(self.relayPeriodInSecs) * time.Second)

	go self.listenForEvents()

	return nil
}

func (self *Metrics) Stop() error {
	self.quitChannel <- true
	return nil
}

// Pass the logger struct to the log metrics.
func NewLogMetrics(logger Logger) *LogMetrics {
	return &LogMetrics{ Logger: logger, enabled: true }
}

// This can be used to print metrics out to a configured logger.
type LogMetrics struct {
	Logger
	enabled bool
}

func (self *LogMetrics) Disable() {
	self.enabled = false
}

func (self *LogMetrics) Enable() {
	self.enabled = true
}

// This logs an info message with the following format: [source: %s - type: %s - metric: %s - value: %f]
func (self *LogMetrics) Log(sourceName string, metrics []Metric) {

	if !self.enabled {
		return
	}

	var typeStr string
	for i := range metrics {
		switch metrics[i].Type {
			case Counter: typeStr = CounterStr
			case Gauge: typeStr = GaugeStr
		}
		self.Logf(Info, "[source: %s - type: %s - metric: %s - value: %f]", sourceName, typeStr, metrics[i].Name, metrics[i].Value)
	}
}

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

package dlshared

import "time"

type MetricsRelayFunction func(string, []Metric)

type metricType int8

const (
	Counter metricType = 0
	Gauge metricType = 1

	CounterStr = "counter"
	GaugeStr = "gauge"
)

type Metric struct {
	Name string
	Type metricType
	Value float64
}

type Metrics struct {
	sourceName string
	quitChannel chan bool
	relayFunc MetricsRelayFunction
	relayPeriodInSecs int
	metricChannel chan *Metric
	ticker *time.Ticker
}

// The relay function is only called if there are metrics to relay.
func NewMetrics(	sourceName string,
					relayFunc MetricsRelayFunction,
					relayPeriodInSecs int,
					metricQueueLength int) *Metrics {

	return &Metrics{
		sourceName : sourceName,
		relayFunc : relayFunc,
		relayPeriodInSecs: relayPeriodInSecs,
		quitChannel : make(chan bool),
		metricChannel : make(chan *Metric, metricQueueLength),
	}
}

// Update the gauge value
func (self *Metrics) Gauge(metricName string, value float64) {
	self.metricChannel <- &Metric{
		Name: metricName,
		Type: Gauge,
		Value: value,
	}
}

// Increases the counter by one.
func (self *Metrics) Count(metricName string) {
	self.metricChannel <- &Metric{
		Name: metricName,
		Type: Counter,
		Value: 1,
	}
}

// Increase the counter
func (self *Metrics) CountWithValue(metricName string, value float64) {
	self.metricChannel <- &Metric{
		Name: metricName,
		Type: Counter,
		Value: value,
	}
}

func (self *Metrics) listenForEvents() {

	metrics := make(map[string]Metric)

    for {
        select {
			case metric := <- self.metricChannel:
				current, found := metrics[metric.Name]
				if !found {
					metrics[metric.Name] = *metric
					continue
				}

				if metric.Type == Counter {
					current.Value = current.Value + metric.Value
				} else {
					// This is a gauge
					current.Value = metric.Value
				}

			case <- self.ticker.C:

				var toRelay []Metric

				for _, v := range metrics {
					toRelay = append(toRelay, Metric{ Name: v.Name, Type: v.Type, Value: v.Value })
				}

				if len(toRelay) == 0 {
					continue
				}

				go self.relayFunc(self.sourceName, toRelay)

			case <- self.quitChannel:
				self.ticker.Stop()
				return
        }
    }
}

func (self *Metrics) Start() error {

	self.ticker = time.NewTicker(time.Duration(self.relayPeriodInSecs) * time.Second)

	go self.listenForEvents()

	return nil
}

func (self *Metrics) Stop() error {
	self.quitChannel <- true
	return nil
}

// Pass the logger struct to the log metrics.
func NewLogMetrics(logger Logger) *LogMetrics {
	return &LogMetrics{ Logger: logger, enabled: true }
}

// This can be used to print metrics out to a configured logger.
type LogMetrics struct {
	Logger
	enabled bool
}

func (self *LogMetrics) Disable() {
	self.enabled = false
}

func (self *LogMetrics) Enable() {
	self.enabled = true
}

// This logs an info message with the following format: [source: %s - type: %s - metric: %s - value: %f]
func (self *LogMetrics) Log(sourceName string, metrics []Metric) {

	if !self.enabled {
		return
	}

	var typeStr string
	for i := range metrics {
		switch metrics[i].Type {
			case Counter: typeStr = CounterStr
			case Gauge: typeStr = GaugeStr
		}
		self.Logf(Info, "[source: %s - type: %s - metric: %s - value: %f]", sourceName, typeStr, metrics[i].Name, metrics[i].Value)
	}
}

<|endoftext|>