<commit_before>package commands

import (
	"fmt"
	"os/exec"
	"regexp"
	"strconv"
	"strings"
	"time"

	"github.com/aisk/logp"
	"github.com/fatih/color"
	"github.com/leancloud/lean-cli/api"
	"github.com/leancloud/lean-cli/apps"
	"github.com/leancloud/lean-cli/console"
	"github.com/leancloud/lean-cli/runtimes"
	"github.com/leancloud/lean-cli/version"
	"github.com/urfave/cli"
)

var (
	errDoNotSupportCloudCode = cli.NewExitError(`命令行工具不再支持 cloudcode 2.0 项目，请参考此文档对您的项目进行升级：
https://leancloud.cn/docs/leanengine_upgrade_3.html`, 1)
)

// get the console port. now console port is just runtime port plus one.
func getConsolePort(runtimePort int) int {
	return runtimePort + 1
}

func upAction(c *cli.Context) error {
	version.PrintCurrentVersion()
	customArgs := c.Args()
	watchChanges := c.Bool("watch")
	customCommand := c.String("cmd")
	rtmPort := c.Int("port")
	consPort := c.Int("console-port")
	if consPort == 0 {
		consPort = getConsolePort(rtmPort)
	}

	appID, err := apps.GetCurrentAppID(".")
	if err != nil {
		return err
	}

	region, err := api.GetAppRegion(appID)
	if err != nil {
		return err
	}

	rtm, err := runtimes.DetectRuntime("")
	if err != nil {
		return err
	}
	rtm.Port = strconv.Itoa(rtmPort)
	rtm.Args = append(rtm.Args, customArgs...)
	if customCommand != "" {
		customCommand = strings.TrimSpace(customCommand)
		cmds := regexp.MustCompile(" +").Split(customCommand, -1)
		rtm.Exec = cmds[0]
		rtm.Args = cmds[1:]
	}

	if watchChanges {
		printDeprecatedWatchWarning(rtm)
	}

	if rtm.Name == "cloudcode" {
		return errDoNotSupportCloudCode
	}

	logp.Info("获取应用信息 ...")
	appInfo, err := api.GetAppInfo(appID)
	if err != nil {
		return err
	}
	logp.Infof("当前应用：%s (%s)\r\n", color.RedString(appInfo.AppName), appID)

	groupName, err := apps.GetCurrentGroup(".")
	if err != nil {
		return err
	}
	groupInfo, err := api.GetGroup(appID, groupName)
	if err != nil {
		return err
	}

	engineInfo, err := api.GetEngineInfo(appID)
	if err != nil {
		return err
	}
	haveStaging := "false"
	if engineInfo.Mode == "prod" {
		haveStaging = "true"
	}

	rtm.Envs = []string{
		"LC_APP_ID=" + appInfo.AppID,
		"LC_APP_KEY=" + appInfo.AppKey,
		"LC_APP_MASTER_KEY=" + appInfo.MasterKey,
		"LC_APP_PORT=" + strconv.Itoa(rtmPort),
		"LC_API_SERVER=" + region.APIServerURL(),
		"LEANCLOUD_APP_ID=" + appInfo.AppID,
		"LEANCLOUD_APP_KEY=" + appInfo.AppKey,
		"LEANCLOUD_APP_MASTER_KEY=" + appInfo.MasterKey,
		"LEANCLOUD_APP_HOOK_KEY=" + appInfo.HookKey,
		"LEANCLOUD_APP_PORT=" + strconv.Itoa(rtmPort),
		"LEANCLOUD_API_SERVER=" + region.APIServerURL(),
		"LEANCLOUD_APP_ENV=" + "development",
		"LEANCLOUD_REGION=" + region.String(),
		"LEANCLOUD_APP_DOMAIN=" + groupInfo.Domain,
		"LEAN_CLI_HAVE_STAGING=" + haveStaging,
	}

	for k, v := range groupInfo.Environments {
		logp.Info("从服务器导出自定义环境变量:", k)
		rtm.Envs = append(rtm.Envs, fmt.Sprintf("%s=%s", k, v))
	}

	cons := &console.Server{
		AppID:       appInfo.AppID,
		AppKey:      appInfo.AppKey,
		MasterKey:   appInfo.MasterKey,
		HookKey:     appInfo.HookKey,
		RemoteURL:   "http://localhost:" + strconv.Itoa(rtmPort),
		ConsolePort: strconv.Itoa(consPort),
		Errors:      make(chan error),
	}

	rtm.Run()
	time.Sleep(time.Millisecond * 300)
	cons.Run()

	for {
		select {
		case err = <-cons.Errors:
			panic(err)
		case err = <-rtm.Errors:
			if _, ok := err.(*exec.ExitError); ok {
				return cli.NewExitError("", 1)
			}
			panic(err)
		}
	}
}

func printDeprecatedWatchWarning(rtm *runtimes.Runtime) {
	logp.Warn("--watch 选项不再被支持，请使用项目代码本身实现此功能")
	if rtm.Name == "python" {
		logp.Warn("可以参考此 Pull Request 来给现有项目增加调试时自动重启功能：")
		logp.Warn("https://github.com/leancloud/python-getting-started/pull/12/files")
	}
	if rtm.Name == "node.js" {
		logp.Warn("可以参考此 Pull Request 来给现有项目增加调试时自动重启功能：")
		logp.Warn("https://github.com/leancloud/node-js-getting-started/pull/26/files")
	}
}
<commit_msg>Fix #381. Use local env var if available.<commit_after>package commands

import (
	"fmt"
	"os"
	"os/exec"
	"regexp"
	"strconv"
	"strings"
	"time"

	"github.com/aisk/logp"
	"github.com/fatih/color"
	"github.com/leancloud/lean-cli/api"
	"github.com/leancloud/lean-cli/apps"
	"github.com/leancloud/lean-cli/console"
	"github.com/leancloud/lean-cli/runtimes"
	"github.com/leancloud/lean-cli/version"
	"github.com/urfave/cli"
)

var (
	errDoNotSupportCloudCode = cli.NewExitError(`命令行工具不再支持 cloudcode 2.0 项目，请参考此文档对您的项目进行升级：
https://leancloud.cn/docs/leanengine_upgrade_3.html`, 1)
)

// get the console port. now console port is just runtime port plus one.
func getConsolePort(runtimePort int) int {
	return runtimePort + 1
}

func upAction(c *cli.Context) error {
	version.PrintCurrentVersion()
	customArgs := c.Args()
	watchChanges := c.Bool("watch")
	customCommand := c.String("cmd")
	rtmPort := c.Int("port")
	consPort := c.Int("console-port")
	if consPort == 0 {
		consPort = getConsolePort(rtmPort)
	}

	appID, err := apps.GetCurrentAppID(".")
	if err != nil {
		return err
	}

	region, err := api.GetAppRegion(appID)
	if err != nil {
		return err
	}

	rtm, err := runtimes.DetectRuntime("")
	if err != nil {
		return err
	}
	rtm.Port = strconv.Itoa(rtmPort)
	rtm.Args = append(rtm.Args, customArgs...)
	if customCommand != "" {
		customCommand = strings.TrimSpace(customCommand)
		cmds := regexp.MustCompile(" +").Split(customCommand, -1)
		rtm.Exec = cmds[0]
		rtm.Args = cmds[1:]
	}

	if watchChanges {
		printDeprecatedWatchWarning(rtm)
	}

	if rtm.Name == "cloudcode" {
		return errDoNotSupportCloudCode
	}

	logp.Info("获取应用信息 ...")
	appInfo, err := api.GetAppInfo(appID)
	if err != nil {
		return err
	}
	logp.Infof("当前应用：%s (%s)\r\n", color.RedString(appInfo.AppName), appID)

	groupName, err := apps.GetCurrentGroup(".")
	if err != nil {
		return err
	}
	groupInfo, err := api.GetGroup(appID, groupName)
	if err != nil {
		return err
	}

	engineInfo, err := api.GetEngineInfo(appID)
	if err != nil {
		return err
	}
	haveStaging := "false"
	if engineInfo.Mode == "prod" {
		haveStaging = "true"
	}

	rtm.Envs = []string{
		"LC_APP_ID=" + appInfo.AppID,
		"LC_APP_KEY=" + appInfo.AppKey,
		"LC_APP_MASTER_KEY=" + appInfo.MasterKey,
		"LC_APP_PORT=" + strconv.Itoa(rtmPort),
		"LC_API_SERVER=" + region.APIServerURL(),
		"LEANCLOUD_APP_ID=" + appInfo.AppID,
		"LEANCLOUD_APP_KEY=" + appInfo.AppKey,
		"LEANCLOUD_APP_MASTER_KEY=" + appInfo.MasterKey,
		"LEANCLOUD_APP_HOOK_KEY=" + appInfo.HookKey,
		"LEANCLOUD_APP_PORT=" + strconv.Itoa(rtmPort),
		"LEANCLOUD_API_SERVER=" + region.APIServerURL(),
		"LEANCLOUD_APP_ENV=" + "development",
		"LEANCLOUD_REGION=" + region.String(),
		"LEANCLOUD_APP_DOMAIN=" + groupInfo.Domain,
		"LEAN_CLI_HAVE_STAGING=" + haveStaging,
	}

	for k, v := range groupInfo.Environments {
		localVar := os.Getenv(k);
		if localVar == "" {
			logp.Info("从服务器导出自定义环境变量：", k)
			rtm.Envs = append(rtm.Envs, fmt.Sprintf("%s=%s", k, v))
		} else {
			logp.Info("使用本地环境变量：", k)
			rtm.Envs = append(rtm.Envs, fmt.Sprintf("%s=%s", k, localVar))
		}
	}

	cons := &console.Server{
		AppID:       appInfo.AppID,
		AppKey:      appInfo.AppKey,
		MasterKey:   appInfo.MasterKey,
		HookKey:     appInfo.HookKey,
		RemoteURL:   "http://localhost:" + strconv.Itoa(rtmPort),
		ConsolePort: strconv.Itoa(consPort),
		Errors:      make(chan error),
	}

	rtm.Run()
	time.Sleep(time.Millisecond * 300)
	cons.Run()

	for {
		select {
		case err = <-cons.Errors:
			panic(err)
		case err = <-rtm.Errors:
			if _, ok := err.(*exec.ExitError); ok {
				return cli.NewExitError("", 1)
			}
			panic(err)
		}
	}
}

func printDeprecatedWatchWarning(rtm *runtimes.Runtime) {
	logp.Warn("--watch 选项不再被支持，请使用项目代码本身实现此功能")
	if rtm.Name == "python" {
		logp.Warn("可以参考此 Pull Request 来给现有项目增加调试时自动重启功能：")
		logp.Warn("https://github.com/leancloud/python-getting-started/pull/12/files")
	}
	if rtm.Name == "node.js" {
		logp.Warn("可以参考此 Pull Request 来给现有项目增加调试时自动重启功能：")
		logp.Warn("https://github.com/leancloud/node-js-getting-started/pull/26/files")
	}
}
<|endoftext|>


<commit_before>package opencv

import (
	"bytes"
	"io/ioutil"
	"os"
	"path"
	"runtime"
	"syscall"
	"testing"
)

func TestLoadImage2(t *testing.T) {
	// t.Errorf("aaa")
}

func TestInitFont(t *testing.T) {
	// Will assert at the C layer on error
	InitFont(CV_FONT_HERSHEY_DUPLEX, 1, 1, 0, 1, 8)
}

func TestPutText(t *testing.T) {
	_, currentfile, _, _ := runtime.Caller(0)
	filename := path.Join(path.Dir(currentfile), "../images/lena.jpg")

	image := LoadImage(filename)
	if image == nil {
		t.Fatal("LoadImage fail")
	}
	defer image.Release()

	// Write 'Hello' on the image
	font := InitFont(CV_FONT_HERSHEY_DUPLEX, 1, 1, 0, 1, 8)
	color := NewScalar(255, 255, 255, 0)

	pos := Point{image.Width() / 2, image.Height() / 2}
	font.PutText(image, "Hello", pos, color)

	filename = path.Join(path.Dir(currentfile), "../images/lena_with_text.jpg")

	// Uncomment this code to create the test image "../images/lena_with_text.jpg"
	// It is part of the repo, and what this test compares against
	//
	// SaveImage(filename, image, 0)
	// println("Saved file", filename)

	tempfilename := path.Join(os.TempDir(), "lena_with_text.jpg")
	defer syscall.Unlink(tempfilename)

	SaveImage(tempfilename, image, 0)

	// Compare actual image with expected image
	same, err := BinaryCompare(filename, tempfilename)
	if err != nil {
		t.Fatal(err)
	}
	if !same {
		t.Error("Actual file differs from expected file with text")
	}
}

// Compare two files, return true if exactly the same
func BinaryCompare(file1, file2 string) (bool, error) {
	f1, err := ioutil.ReadFile(file1)
	if err != nil {
		return false, err
	}

	f2, err := ioutil.ReadFile(file2)
	if err != nil {
		return false, err
	}

	return bytes.Equal(f1, f2), nil
}

func TestAbsDiff(t *testing.T) {
	_, currentfile, _, _ := runtime.Caller(0)
	filename := path.Join(path.Dir(currentfile), "../images/lena.jpg")

	org := LoadImage(filename)
	modified := LoadImage(filename)
	diff := CreateImage(org.Width(), org.Height(), IPL_DEPTH_8U, 3)

	if org == nil || modified == nil {
		t.Fatal("LoadImage fail")
	}
	defer org.Release()
	defer modified.Release()
	defer diff.Release()

	// Write 'Hello' on the image
	font := InitFont(CV_FONT_HERSHEY_DUPLEX, 1, 1, 0, 1, 8)
	color := NewScalar(255, 255, 255, 0)

	pos := Point{modified.Width() / 2, modified.Height() / 2}
	font.PutText(modified, "Hello", pos, color)

	// diff the images witwh hello on it and the original one
	AbsDiff(org, modified, diff)

	// very basic checking, most of the image should be black and only
	// the "hello" pixels should remain. We should expect this many
	// black pixels = 260766
	black_pixels := 0

	for x := 0; x < diff.Width()-1; x++ {
		for y := 0; y < diff.Height()-1; y++ {
			pixel := diff.Get2D(x, y).Val()

			if pixel[0] == 0.0 && pixel[1] == 0.0 && pixel[2] == 0.0 {
				black_pixels++
			}
		}
	}

	if black_pixels != 260766 {
		t.Error("Unexpected result for AbsDiff")
	}
}
<commit_msg>test for adding and subtracting<commit_after>package opencv

import (
	"bytes"
	"io/ioutil"
	"os"
	"path"
	"runtime"
	"syscall"
	"testing"
)

func TestLoadImage2(t *testing.T) {
	// t.Errorf("aaa")
}

func TestInitFont(t *testing.T) {
	// Will assert at the C layer on error
	InitFont(CV_FONT_HERSHEY_DUPLEX, 1, 1, 0, 1, 8)
}

func TestPutText(t *testing.T) {
	_, currentfile, _, _ := runtime.Caller(0)
	filename := path.Join(path.Dir(currentfile), "../images/lena.jpg")

	image := LoadImage(filename)
	if image == nil {
		t.Fatal("LoadImage fail")
	}
	defer image.Release()

	// Write 'Hello' on the image
	font := InitFont(CV_FONT_HERSHEY_DUPLEX, 1, 1, 0, 1, 8)
	color := NewScalar(255, 255, 255, 0)

	pos := Point{image.Width() / 2, image.Height() / 2}
	font.PutText(image, "Hello", pos, color)

	filename = path.Join(path.Dir(currentfile), "../images/lena_with_text.jpg")

	// Uncomment this code to create the test image "../images/lena_with_text.jpg"
	// It is part of the repo, and what this test compares against
	//
	// SaveImage(filename, image, 0)
	// println("Saved file", filename)

	tempfilename := path.Join(os.TempDir(), "lena_with_text.jpg")
	defer syscall.Unlink(tempfilename)

	SaveImage(tempfilename, image, 0)

	// Compare actual image with expected image
	same, err := BinaryCompare(filename, tempfilename)
	if err != nil {
		t.Fatal(err)
	}
	if !same {
		t.Error("Actual file differs from expected file with text")
	}
}

// Compare two files, return true if exactly the same
func BinaryCompare(file1, file2 string) (bool, error) {
	f1, err := ioutil.ReadFile(file1)
	if err != nil {
		return false, err
	}

	f2, err := ioutil.ReadFile(file2)
	if err != nil {
		return false, err
	}

	return bytes.Equal(f1, f2), nil
}

func TestAbsDiff(t *testing.T) {
	_, currentfile, _, _ := runtime.Caller(0)
	filename := path.Join(path.Dir(currentfile), "../images/lena.jpg")

	org := LoadImage(filename)
	modified := LoadImage(filename)
	diff := CreateImage(org.Width(), org.Height(), IPL_DEPTH_8U, 3)

	if org == nil || modified == nil {
		t.Fatal("LoadImage fail")
	}
	defer org.Release()
	defer modified.Release()
	defer diff.Release()

	// Write 'Hello' on the image
	font := InitFont(CV_FONT_HERSHEY_DUPLEX, 1, 1, 0, 1, 8)
	color := NewScalar(255, 255, 255, 0)

	pos := Point{modified.Width() / 2, modified.Height() / 2}
	font.PutText(modified, "Hello", pos, color)

	// diff the images witwh hello on it and the original one
	AbsDiff(org, modified, diff)

	// very basic checking, most of the image should be black and only
	// the "hello" pixels should remain. We should expect this many
	// black pixels = 260766
	black_pixels := 0

	for x := 0; x < diff.Width()-1; x++ {
		for y := 0; y < diff.Height()-1; y++ {
			pixel := diff.Get2D(x, y).Val()

			if pixel[0] == 0.0 && pixel[1] == 0.0 && pixel[2] == 0.0 {
				black_pixels++
			}
		}
	}

	if black_pixels != 260766 {
		t.Error("Unexpected result for AbsDiff")
	}
}

func TestAddSub(t *testing.T) {

	checkVals := func(img *IplImage, val float64, debug string) {
	loop:
		for j := 0; j < img.Height(); j++ {
			for i := 0; i < img.Width(); i++ {
				pix := img.Get2D(i, j).Val()
				if pix[0] != val || pix[1] != val || pix[2] != val {
					t.Errorf("Unexpeted value for %s: %.1f, %.1f, %.1f. Expected %.1fs",
						debug, pix[0], pix[1], pix[2], val)
					break loop
				}
			}
		}
	}

	zeroImg := CreateImage(50, 50, IPL_DEPTH_8U, 3)
	zeroImg.Zero()

	twosImg := zeroImg.Clone()
	foursImg := zeroImg.Clone()
	negTwosImg := zeroImg.Clone()
	defer zeroImg.Release()
	defer twosImg.Release()
	defer foursImg.Release()
	defer negTwosImg.Release()

	two := NewScalar(2, 2, 2, 2)

	// 0 + 2 = 2
	AddScalar(zeroImg, two, twosImg)
	checkVals(twosImg, 2, "AddScalar()")

	// 2 + 2 = 4
	Add(twosImg, twosImg, foursImg)
	checkVals(foursImg, 4, "Add()")

	// 4 - 2 = 2
	Subtract(foursImg, twosImg, twosImg)
	checkVals(twosImg, 2, "Sub()")

	// 2 - 2 = 0
	SubScalar(twosImg, two, zeroImg)
	checkVals(zeroImg, 0, "SubScalar()")

	// 2 - 4 = 0 != -2 because it clips
	SubScalarRev(two, foursImg, negTwosImg)
	checkVals(negTwosImg, 0, "SubScalarRev()")

}
<|endoftext|>


<commit_before>package main

import (
	"encoding/binary"
	"flag"
	"log"
	"time"

	"github.com/stampzilla/stampzilla-go/nodes/basenode"
	"github.com/stampzilla/stampzilla-go/protocol"
)

// MAIN - This is run when the init function is done
func main() {
	log.Println("Starting modbus node")

	// Parse all commandline arguments, host and port parameters are added in the basenode init function
	flag.Parse()

	//Get a config with the correct parameters
	config := basenode.NewConfig()

	//Activate the config
	basenode.SetConfig(config)

	node := protocol.NewNode("modbus")

	registers := NewRegisters()
	registers.ReadFromFile("registers.json")

	modbusConnection := &Modbus{}
	err := modbusConnection.Connect()

	if err != nil {
		log.Println(err)
		return
	}

	defer modbusConnection.Close()

	//REG_HC_TEMP_IN1 214 Reg
	//REG_HC_TEMP_IN2 215 Reg
	//REG_HC_TEMP_IN3 216 Reg
	//REG_HC_TEMP_IN4 217 Reg
	//REG_HC_TEMP_IN5 218 Reg

	//REG_DAMPER_PWM 301 Reg
	//REG_HC_WC_SIGNAL 204 Reg

	//client := modbus.NewClient(handler)
	//modbus.NewClient
	//results, _ := client.ReadHoldingRegisters(214, 1)
	//if err != nil {
	//log.Println(err)
	//}
	results, _ := modbusConnection.ReadInputRegister(214)
	log.Println("REG_HC_TEMP_IN1: ", results)
	results, _ = modbusConnection.ReadInputRegister(215)
	log.Println("REG_HC_TEMP_IN2: ", results)
	results, _ = modbusConnection.ReadInputRegister(216)
	log.Println("REG_HC_TEMP_IN3: ", results)
	results, _ = modbusConnection.ReadInputRegister(217)
	log.Println("REG_HC_TEMP_IN4: ", results)
	results, _ = modbusConnection.ReadInputRegister(218)
	log.Println("REG_HC_TEMP_IN5: ", binary.BigEndian.Uint16(results))
	results, _ = modbusConnection.ReadInputRegister(207)
	log.Println("REG_HC_TEMP_LVL: ", results)
	results, _ = modbusConnection.ReadInputRegister(301)
	log.Println("REG_DAMPER_PWM: ", results)
	results, _ = modbusConnection.ReadInputRegister(204)
	log.Println("REG_HC_WC_SIGNAL: ", results)
	results, _ = modbusConnection.ReadInputRegister(209)
	log.Println("REG_HC_TEMP_LVL1-5: ", results)
	results, _ = modbusConnection.ReadInputRegister(101)
	log.Println("100 REG_FAN_SPEED_LEVEL: ", results)

	//Start communication with the server
	connection := basenode.Connect()

	// Thit worker keeps track on our connection state, if we are connected or not

	//node.AddElement(&protocol.Element{
	//Type: protocol.ElementTypeColorPicker,
	//Name: "Example color picker",
	//Command: &protocol.Command{
	//Cmd:  "color",
	//Args: []string{"1"},
	//},
	//Feedback: "Devices[4].State",
	//})

	//state := NewState()
	node.SetState(registers)

	// This worker recives all incomming commands
	go serverRecv(registers, connection, modbusConnection)
	go monitorState(node, connection, registers, modbusConnection)
	select {}
}

// WORKER that monitors the current connection state
func monitorState(node *protocol.Node, connection *basenode.Connection, registers *Registers, modbusConnection *Modbus) {
	var stopFetching chan bool
	for s := range connection.State {
		switch s {
		case basenode.ConnectionStateConnected:
			fetchRegisters(registers, modbusConnection)
			stopFetching = periodicalFetcher(registers, modbusConnection, connection, node)
			connection.Send <- node.Node()
		case basenode.ConnectionStateDisconnected:
			close(stopFetching)
		}
	}
}

func periodicalFetcher(registers *Registers, connection *Modbus, nodeConn *basenode.Connection, node *protocol.Node) chan bool {

	ticker := time.NewTicker(30 * time.Second)
	quit := make(chan bool)
	go func() {
		for {
			select {
			case <-ticker.C:
				fetchRegisters(registers, connection)
				nodeConn.Send <- node.Node()
			case <-quit:
				ticker.Stop()
				log.Println("Stopping periodicalFetcher")
				return
			}
		}
	}()

	return quit
}

func fetchRegisters(registers *Registers, connection *Modbus) {
	for _, v := range registers.Registers {

		data, err := connection.ReadInputRegister(v.Id)
		if err != nil {
			log.Println(err)
			continue
		}
		if len(data) != 2 {
			log.Println("Wrong length, expected 2")
			continue
		}

		if v.Base != 0 {
			v.Value = float64(binary.BigEndian.Uint16(data)) / float64(v.Base)
			continue
		}
		v.Value = binary.BigEndian.Uint16(data)
	}
}

// WORKER that recives all incomming commands
func serverRecv(registers *Registers, connection *basenode.Connection, modbusConnection *Modbus) {
	for d := range connection.Receive {
		processCommand(registers, connection, d)
	}
}

// THis is called on each incomming command
func processCommand(registers *Registers, connection *basenode.Connection, cmd protocol.Command) {
	//if s, ok := node.State.(*Registers); ok {
	//log.Println("Incoming command from server:", cmd)
	//if len(cmd.Args) == 0 {
	//return
	//}
	//device := s.Device(cmd.Args[0])

	//switch cmd.Cmd {
	//case "on":
	//device.State = true
	//connection.Send <- node.Node()
	//case "off":
	//device.State = false
	//connection.Send <- node.Node()
	//case "toggle":
	//log.Println("got toggle")
	//if device.State {
	//device.State = false
	//} else {
	//device.State = true
	//}
	//connection.Send <- node.Node()
	//}
	//}
}
<commit_msg>start debug modbus if we get an error on modbus read<commit_after>package main

import (
	"encoding/binary"
	"flag"
	"log"
	"os"
	"time"

	"github.com/stampzilla/stampzilla-go/nodes/basenode"
	"github.com/stampzilla/stampzilla-go/protocol"
)

// MAIN - This is run when the init function is done
func main() {
	log.Println("Starting modbus node")

	// Parse all commandline arguments, host and port parameters are added in the basenode init function
	flag.Parse()

	//Get a config with the correct parameters
	config := basenode.NewConfig()

	//Activate the config
	basenode.SetConfig(config)

	node := protocol.NewNode("modbus")

	registers := NewRegisters()
	registers.ReadFromFile("registers.json")

	modbusConnection := &Modbus{}
	err := modbusConnection.Connect()

	if err != nil {
		log.Println(err)
		return
	}

	defer modbusConnection.Close()

	//REG_HC_TEMP_IN1 214 Reg
	//REG_HC_TEMP_IN2 215 Reg
	//REG_HC_TEMP_IN3 216 Reg
	//REG_HC_TEMP_IN4 217 Reg
	//REG_HC_TEMP_IN5 218 Reg

	//REG_DAMPER_PWM 301 Reg
	//REG_HC_WC_SIGNAL 204 Reg

	//client := modbus.NewClient(handler)
	//modbus.NewClient
	//results, _ := client.ReadHoldingRegisters(214, 1)
	//if err != nil {
	//log.Println(err)
	//}
	results, _ := modbusConnection.ReadInputRegister(214)
	log.Println("REG_HC_TEMP_IN1: ", results)
	results, _ = modbusConnection.ReadInputRegister(215)
	log.Println("REG_HC_TEMP_IN2: ", results)
	results, _ = modbusConnection.ReadInputRegister(216)
	log.Println("REG_HC_TEMP_IN3: ", results)
	results, _ = modbusConnection.ReadInputRegister(217)
	log.Println("REG_HC_TEMP_IN4: ", results)
	results, _ = modbusConnection.ReadInputRegister(218)
	log.Println("REG_HC_TEMP_IN5: ", binary.BigEndian.Uint16(results))
	results, _ = modbusConnection.ReadInputRegister(207)
	log.Println("REG_HC_TEMP_LVL: ", results)
	results, _ = modbusConnection.ReadInputRegister(301)
	log.Println("REG_DAMPER_PWM: ", results)
	results, _ = modbusConnection.ReadInputRegister(204)
	log.Println("REG_HC_WC_SIGNAL: ", results)
	results, _ = modbusConnection.ReadInputRegister(209)
	log.Println("REG_HC_TEMP_LVL1-5: ", results)
	results, _ = modbusConnection.ReadInputRegister(101)
	log.Println("100 REG_FAN_SPEED_LEVEL: ", results)

	//Start communication with the server
	connection := basenode.Connect()

	// Thit worker keeps track on our connection state, if we are connected or not

	//node.AddElement(&protocol.Element{
	//Type: protocol.ElementTypeColorPicker,
	//Name: "Example color picker",
	//Command: &protocol.Command{
	//Cmd:  "color",
	//Args: []string{"1"},
	//},
	//Feedback: "Devices[4].State",
	//})

	//state := NewState()
	node.SetState(registers)

	// This worker recives all incomming commands
	go serverRecv(registers, connection, modbusConnection)
	go monitorState(node, connection, registers, modbusConnection)
	select {}
}

// WORKER that monitors the current connection state
func monitorState(node *protocol.Node, connection *basenode.Connection, registers *Registers, modbusConnection *Modbus) {
	var stopFetching chan bool
	for s := range connection.State {
		switch s {
		case basenode.ConnectionStateConnected:
			fetchRegisters(registers, modbusConnection)
			stopFetching = periodicalFetcher(registers, modbusConnection, connection, node)
			connection.Send <- node.Node()
		case basenode.ConnectionStateDisconnected:
			close(stopFetching)
		}
	}
}

func periodicalFetcher(registers *Registers, connection *Modbus, nodeConn *basenode.Connection, node *protocol.Node) chan bool {

	ticker := time.NewTicker(30 * time.Second)
	quit := make(chan bool)
	go func() {
		for {
			select {
			case <-ticker.C:
				fetchRegisters(registers, connection)
				nodeConn.Send <- node.Node()
			case <-quit:
				ticker.Stop()
				log.Println("Stopping periodicalFetcher")
				return
			}
		}
	}()

	return quit
}

func fetchRegisters(registers *Registers, connection *Modbus) {
	for _, v := range registers.Registers {

		data, err := connection.ReadInputRegister(v.Id)
		if err != nil {
			if connection.handler.Logger == nil {
				log.Println("Adding debug logging to handler")
				connection.handler.Logger = log.New(os.Stdout, "modbus-debug: ", log.LstdFlags)
			}
			log.Println(err)
			continue
		}
		if len(data) != 2 {
			log.Println("Wrong length, expected 2")
			continue
		}

		if v.Base != 0 {
			v.Value = float64(binary.BigEndian.Uint16(data)) / float64(v.Base)
			continue
		}
		v.Value = binary.BigEndian.Uint16(data)
	}
}

// WORKER that recives all incomming commands
func serverRecv(registers *Registers, connection *basenode.Connection, modbusConnection *Modbus) {
	for d := range connection.Receive {
		processCommand(registers, connection, d)
	}
}

// THis is called on each incomming command
func processCommand(registers *Registers, connection *basenode.Connection, cmd protocol.Command) {
	//if s, ok := node.State.(*Registers); ok {
	//log.Println("Incoming command from server:", cmd)
	//if len(cmd.Args) == 0 {
	//return
	//}
	//device := s.Device(cmd.Args[0])

	//switch cmd.Cmd {
	//case "on":
	//device.State = true
	//connection.Send <- node.Node()
	//case "off":
	//device.State = false
	//connection.Send <- node.Node()
	//case "toggle":
	//log.Println("got toggle")
	//if device.State {
	//device.State = false
	//} else {
	//device.State = true
	//}
	//connection.Send <- node.Node()
	//}
	//}
}
<|endoftext|>


<commit_before>// Binary stfortune is a simple client of Veyron Store.  See
// http://go/veyron:codelab-store for a thorough explanation.
package main

import (
	"crypto/md5"
	"encoding/hex"
	"flag"
	"fmt"
	"io"
	"log"
	"math/rand"
	"os"
	"strings"
	"time"

	"veyron/examples/stfortune/schema"

	"veyron2/context"
	"veyron2/naming"
	"veyron2/rt"
	istore "veyron2/services/store"
	iwatch "veyron2/services/watch"
	"veyron2/storage"
	"veyron2/storage/vstore"
	"veyron2/storage/vstore/primitives"
	"veyron2/vom"
)

func fortunePath(name string) string {
	return naming.Join(naming.Join(appPath, "fortunes"), name)
}

func userPath(name string) string {
	return naming.Join(naming.Join(appPath, "usernames"), name)
}

// Hashes a string.
func getMD5Hash(text string) string {
	hasher := md5.New()
	hasher.Write([]byte(text))
	return hex.EncodeToString(hasher.Sum(nil))
}

// waitForStore waits for the local store to be ready by checking if
// the schema information is synchronized.
func waitForStore(store storage.Store) {
	ctx := rt.R().NewContext()

	// Register *store.Entry for WatchGlob.
	// TODO(tilaks): store.Entry is declared in vdl, vom should register the
	// pointer automatically.
	vom.Register(&istore.Entry{})

	fmt.Printf("Waiting for Store to be initialized with fortune schema...\n")
	// List of paths to check in store.
	paths := []string{appPath, fortunePath(""), userPath("")}
	for _, path := range paths {
		req := iwatch.GlobRequest{Pattern: ""}
		stream, err := store.Bind(path).WatchGlob(ctx, req)
		if err != nil {
			log.Fatalf("WatchGlob %s failed: %v", path, err)
		}
		if !stream.Advance() {
			log.Fatalf("Advance failed: %v", stream.Err())
		}
		stream.Cancel()
	}

	fmt.Printf("Store is ready\n")
	return
}

// runAsWatcher monitors updates to the fortunes in the store and
// prints out that information.  It does not return.
func runAsWatcher(store storage.Store, user string) {
	// TODO(tilaks): remove this when the store.Entry is auto-registered by VOM.
	vom.Register(&istore.Entry{})
	ctx := rt.R().NewContext()

	// Monitor all new fortunes or only those of a specific user.
	var path string
	if user == "" {
		path = fortunePath("")
	} else {
		path = userPath(user)
	}
	fmt.Printf("Running as a Watcher monitoring new fortunes under %s...\n", path)

	req := iwatch.GlobRequest{Pattern: "*"}
	stream, err := store.Bind(path).WatchGlob(ctx, req)
	if err != nil {
		log.Fatalf("watcher WatchGlob %s failed: %v", path, err)
	}

	for stream.Advance() {
		batch := stream.Value()

		for _, change := range batch.Changes {
			entry, ok := change.Value.(*storage.Entry)
			if !ok {
				log.Printf("watcher change Value not a storage Entry: %#v", change.Value)
				continue
			}

			fortune, ok := entry.Value.(schema.FortuneData)
			if !ok {
				log.Printf("watcher data not a FortuneData Entry: %#v", entry.Value)
				continue
			}

			fmt.Printf("watcher: new fortune: %s\n", fortune.Fortune)
		}
	}
	err = stream.Err()
	if err == nil {
		err = io.EOF
	}
	log.Fatalf("watcher Advance failed: %v", err)
}

// pickFortune finds all available fortunes under the input path and
// chooses one randomly.
func pickFortune(store storage.Store, ctx context.T, path string) (string, error) {
	tr := primitives.NewTransaction(ctx)
	defer tr.Abort(ctx)

	results, err := store.Bind(path).GlobT(ctx, tr, "*")
	if err != nil {
		return "", err
	}
	var names []string
	for results.Advance() {
		name := results.Value()
		names = append(names, name)
	}
	results.Finish()
	if names == nil || len(names) < 1 {
		return "", nil
	}

	// Get a random fortune using the glob results.
	random := rand.New(rand.NewSource(time.Now().UTC().UnixNano()))
	p := fortunePath(names[random.Intn(len(names))])
	f, err := store.Bind(p).Get(ctx, tr)
	if err != nil {
		return "", err
	}
	fortune, ok := f.Value.(schema.FortuneData)
	if !ok {
		return "", fmt.Errorf("found type %T, expected schema.FortuneData", f.Value)
	}
	return fortune.Fortune, nil
}

// getFortune returns a random fortune corresponding to a UserName if
// specified. If not, it picks a random fortune.
func getFortune(store storage.Store, userName string) (string, error) {
	ctx := rt.R().NewContext()

	var p string
	if userName != "" {
		// Look for a random fortune belonging to UserName.
		p = userPath(userName)
	} else {
		// Look for a random fortune.
		p = fortunePath("")
	}

	return pickFortune(store, ctx, p)
}

// addFortune adds a new fortune to the store and links it to the specified
// UserName. In this process, if the UserName doesn't exist, a new
// user is created.
func addFortune(store storage.Store, fortune string, userName string) error {
	ctx := rt.R().NewContext()
	tr := primitives.NewTransaction(ctx)
	committed := false
	defer func() {
		if !committed {
			tr.Abort(ctx)
		}
	}()

	// Check if this fortune already exists. If yes, return.
	hash := getMD5Hash(naming.Join(fortune, userName))
	exists, err := store.Bind(fortunePath(hash)).Exists(ctx, tr)
	if err != nil {
		return err
	}
	if exists {
		return nil
	}

	// Check if the UserName exists. If yes, get its OID. If not, create a new user.
	o := store.Bind(userPath(userName))
	exists, err = o.Exists(ctx, tr)
	if err != nil {
		return err
	}
	var userid storage.ID
	if !exists {
		u := schema.User{Name: userName}
		stat, err := o.Put(ctx, tr, u)
		if err != nil {
			return err
		}
		userid = stat.ID
	} else {
		u, err := o.Get(ctx, tr)
		if err != nil {
			return err
		}
		userid = u.Stat.ID
	}

	// Create a new fortune entry.
	f := schema.FortuneData{Fortune: fortune, UserName: userid}
	s, err := store.Bind(fortunePath(hash)).Put(ctx, tr, f)
	if err != nil {
		return err
	}

	// Link the new fortune to UserName.
	p := userPath(naming.Join(userName, hash))
	if _, err = store.Bind(p).Put(ctx, tr, s.ID); err != nil {
		return err
	}

	// Commit all changes.
	//
	// NOTE: A commit can sometimes fail due to store's optimistic
	// locking. When the error for this scenario is
	// exposed via the Commit API, one could retry the
	// transaction.
	if err := tr.Commit(ctx); err != nil {
		return err
	}
	committed = true
	return nil
}

var (
	appPath      = "/apps/stfortune"
	storeAddress = flag.String("store", "", "the address/endpoint of the Veyron Store")
	newFortune   = flag.String("new_fortune", "", "an optional, new fortune to add to the server's set")
	user         = flag.String("user_name", "", "an optional username of the fortune creator to get/add to the server's set")
	watch        = flag.Bool("watch", false, "run as a watcher reporting new fortunes")
)

func main() {
	rt.Init()
	if *storeAddress == "" {
		log.Fatal("--store needs to be specified")
	}

	// Create a handle to the backend store.
	store, err := vstore.New(*storeAddress)
	if err != nil {
		log.Fatalf("Can't connect to store: %s: %v", *storeAddress, err)
	}

	// Wait for the store to be ready before proceeding.
	waitForStore(store)

	// Get a fortune from the store.
	fortune, err := getFortune(store, *user)
	if err != nil {
		log.Fatal("error getting fortune: ", err)
	}
	fmt.Println("Fortune: ", fortune)

	// If the user specified --new_fortune, add it to the store’s set of fortunes.
	if *newFortune != "" {
		if *user == "" {
			*user = "anonymous"
		}
		*user = strings.ToLower(*user)
		if err := addFortune(store, *newFortune, *user); err != nil {
			log.Fatal("error adding fortune: ", err)
		}
	}

	// Run as a watcher if --watch is set.
	if *watch {
		runAsWatcher(store, *user)
		os.Exit(0)
	}
}
<commit_msg>veyron/examples/stfortune: Use query functionality in the codelab.<commit_after>// Binary stfortune is a simple client of Veyron Store.  See
// http://go/veyron:codelab-store for a thorough explanation.
package main

import (
	"crypto/md5"
	"encoding/hex"
	"flag"
	"fmt"
	"io"
	"log"
	"math/rand"
	"os"
	"strings"
	"time"
	"veyron2/query"

	"veyron/examples/stfortune/schema"

	"veyron2/context"
	"veyron2/naming"
	"veyron2/rt"
	istore "veyron2/services/store"
	iwatch "veyron2/services/watch"
	"veyron2/storage"
	"veyron2/storage/vstore"
	"veyron2/storage/vstore/primitives"
	"veyron2/vom"
)

func fortunePath(name string) string {
	return naming.Join(naming.Join(appPath, "fortunes"), name)
}

func userPath(name string) string {
	return naming.Join(naming.Join(appPath, "usernames"), name)
}

// Hashes a string.
func getMD5Hash(text string) string {
	hasher := md5.New()
	hasher.Write([]byte(text))
	return hex.EncodeToString(hasher.Sum(nil))
}

// waitForStore waits for the local store to be ready by checking if
// the schema information is synchronized.
func waitForStore(store storage.Store) {
	ctx := rt.R().NewContext()

	// Register *store.Entry for WatchGlob.
	// TODO(tilaks): store.Entry is declared in vdl, vom should register the
	// pointer automatically.
	vom.Register(&istore.Entry{})

	fmt.Printf("Waiting for Store to be initialized with fortune schema...\n")
	// List of paths to check in store.
	paths := []string{appPath, fortunePath(""), userPath("")}
	for _, path := range paths {
		req := iwatch.GlobRequest{Pattern: ""}
		stream, err := store.Bind(path).WatchGlob(ctx, req)
		if err != nil {
			log.Fatalf("WatchGlob %s failed: %v", path, err)
		}
		if !stream.Advance() {
			log.Fatalf("waitForStore, path: %s, Advance failed: %v", path, stream.Err())
		}
		stream.Cancel()
	}

	fmt.Printf("Store is ready\n")
	return
}

// runAsWatcher monitors updates to the fortunes in the store and
// prints out that information.  It does not return.
func runAsWatcher(store storage.Store, user string) {
	// TODO(tilaks): remove this when the store.Entry is auto-registered by VOM.
	vom.Register(&istore.Entry{})
	ctx := rt.R().NewContext()

	// Monitor all new fortunes or only those of a specific user.
	var path string
	if user == "" {
		path = fortunePath("")
	} else {
		path = userPath(user)
	}
	fmt.Printf("Running as a Watcher monitoring new fortunes under %s...\n", path)

	req := iwatch.GlobRequest{Pattern: "*"}
	stream, err := store.Bind(path).WatchGlob(ctx, req)
	if err != nil {
		log.Fatalf("watcher WatchGlob %s failed: %v", path, err)
	}

	for stream.Advance() {
		batch := stream.Value()

		for _, change := range batch.Changes {
			entry, ok := change.Value.(*storage.Entry)
			if !ok {
				log.Printf("watcher change Value not a storage Entry: %#v", change.Value)
				continue
			}

			fortune, ok := entry.Value.(schema.FortuneData)
			if !ok {
				log.Printf("watcher data not a FortuneData Entry: %#v", entry.Value)
				continue
			}

			fmt.Printf("watcher: new fortune: %s\n", fortune.Fortune)
		}
	}
	err = stream.Err()
	if err == nil {
		err = io.EOF
	}
	log.Fatalf("watcher Advance failed: %v", err)
}

// pickFortuneGlob uses Glob to find all available fortunes under the input
// path and then it chooses one randomly.
func pickFortuneGlob(store storage.Store, ctx context.T, path string) (string, error) {
	tr := primitives.NewTransaction(ctx)
	defer tr.Abort(ctx)

	results, err := store.Bind(path).GlobT(ctx, tr, "*")
	if err != nil {
		return "", err
	}
	var names []string
	for results.Advance() {
		name := results.Value()
		names = append(names, name)
	}
	results.Finish()
	if names == nil || len(names) < 1 {
		return "", nil
	}

	// Get a random fortune using the glob results.
	random := rand.New(rand.NewSource(time.Now().UTC().UnixNano()))
	p := fortunePath(names[random.Intn(len(names))])
	f, err := store.Bind(p).Get(ctx, tr)
	if err != nil {
		return "", err
	}
	fortune, ok := f.Value.(schema.FortuneData)
	if !ok {
		return "", fmt.Errorf("found type %T, expected schema.FortuneData", f.Value)
	}
	return fortune.Fortune, nil
}

// pickFortuneQuery uses a query to find all available fortunes under the input
// path and choose one randomly.
func pickFortuneQuery(store storage.Store, ctx context.T, path string) (string, error) {
	results := store.Bind(path).Query(ctx, nil,
		query.Query{
			"* |" + // Inspect all children of path.
				"type FortuneData |" + // Include only objects of type FortuneData.
				"{Fortune: Fortune} |" + // Create a new struct containing only the Fortune field.
				"sample(1)", // Randomly select one.
		})
	for results.Advance() {
		f := results.Value().Fields()["Fortune"]
		fortune, ok := f.(string)
		if !ok {
			return "", fmt.Errorf("unexpected type for fortune, got %T, expected string", f)
		}
		results.Cancel()
		return fortune, nil
	}
	if results.Err() != nil {
		return "", results.Err()
	}
	return "", nil // No fortunes found.
}

// getFortune returns a random fortune corresponding to a UserName if
// specified. If not, it picks a random fortune.
func getFortune(store storage.Store, userName string) (string, error) {
	ctx := rt.R().NewContext()

	var p string
	if userName != "" {
		// Look for a random fortune belonging to UserName.
		p = userPath(userName)
	} else {
		// Look for a random fortune.
		p = fortunePath("")
	}

	switch *pickMethod {
	case "glob":
		return pickFortuneGlob(store, ctx, p)
	case "query":
		return pickFortuneQuery(store, ctx, p)
	default:
		return "", fmt.Errorf("unsupported value for --pick_method.  use 'glob' or 'query'")
	}
}

// addFortune adds a new fortune to the store and links it to the specified
// UserName. In this process, if the UserName doesn't exist, a new
// user is created.
func addFortune(store storage.Store, fortune string, userName string) error {
	ctx := rt.R().NewContext()
	tr := primitives.NewTransaction(ctx)
	committed := false
	defer func() {
		if !committed {
			tr.Abort(ctx)
		}
	}()

	// Check if this fortune already exists. If yes, return.
	hash := getMD5Hash(naming.Join(fortune, userName))
	exists, err := store.Bind(fortunePath(hash)).Exists(ctx, tr)
	if err != nil {
		return err
	}
	if exists {
		return nil
	}

	// Check if the UserName exists. If yes, get its OID. If not, create a new user.
	o := store.Bind(userPath(userName))
	exists, err = o.Exists(ctx, tr)
	if err != nil {
		return err
	}
	var userid storage.ID
	if !exists {
		u := schema.User{Name: userName}
		stat, err := o.Put(ctx, tr, u)
		if err != nil {
			return err
		}
		userid = stat.ID
	} else {
		u, err := o.Get(ctx, tr)
		if err != nil {
			return err
		}
		userid = u.Stat.ID
	}

	// Create a new fortune entry.
	f := schema.FortuneData{Fortune: fortune, UserName: userid}
	s, err := store.Bind(fortunePath(hash)).Put(ctx, tr, f)
	if err != nil {
		return err
	}

	// Link the new fortune to UserName.
	p := userPath(naming.Join(userName, hash))
	if _, err = store.Bind(p).Put(ctx, tr, s.ID); err != nil {
		return err
	}

	// Commit all changes.
	//
	// NOTE: A commit can sometimes fail due to store's optimistic
	// locking. When the error for this scenario is
	// exposed via the Commit API, one could retry the
	// transaction.
	if err := tr.Commit(ctx); err != nil {
		return err
	}
	committed = true
	return nil
}

var (
	appPath      = "/apps/stfortune"
	storeAddress = flag.String("store", "", "the address/endpoint of the Veyron Store")
	newFortune   = flag.String("new_fortune", "", "an optional, new fortune to add to the server's set")
	user         = flag.String("user_name", "", "an optional username of the fortune creator to get/add to the server's set")
	watch        = flag.Bool("watch", false, "run as a watcher reporting new fortunes")
	pickMethod   = flag.String("pick_method", "glob", "use 'glob' or 'query' to randomly select a fortune")
)

func main() {
	rt.Init()
	if *storeAddress == "" {
		log.Fatal("--store needs to be specified")
	}

	// Create a handle to the backend store.
	store, err := vstore.New(*storeAddress)
	if err != nil {
		log.Fatalf("Can't connect to store: %s: %v", *storeAddress, err)
	}

	// Wait for the store to be ready before proceeding.
	waitForStore(store)

	// Get a fortune from the store.
	fortune, err := getFortune(store, *user)
	if err != nil {
		log.Fatal("error getting fortune: ", err)
	}
	fmt.Println("Fortune: ", fortune)

	// If the user specified --new_fortune, add it to the store’s set of fortunes.
	if *newFortune != "" {
		if *user == "" {
			*user = "anonymous"
		}
		*user = strings.ToLower(*user)
		if err := addFortune(store, *newFortune, *user); err != nil {
			log.Fatal("error adding fortune: ", err)
		}
	}

	// Run as a watcher if --watch is set.
	if *watch {
		runAsWatcher(store, *user)
		os.Exit(0)
	}
}
<|endoftext|>


<commit_before>package qingcloud

import (
	"fmt"
	"log"
	"testing"

	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
	"github.com/hashicorp/terraform/terraform"
	qc "github.com/yunify/qingcloud-sdk-go/service"
)

func TestAccQingcloudEIP_basic(t *testing.T) {
	var eip qc.DescribeEIPsOutput

	resource.Test(t, resource.TestCase{
		PreCheck: func() {
			testAccPreCheck(t)
		},

		// module name
		IDRefreshName: "qingcloud_eip.foo",
		Providers:     testAccProviders,
		CheckDestroy:  testAccCheckEIPDestroy,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccEIPConfig,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckEIPExists(
						"qingcloud_eip.foo", &eip),
					resource.TestCheckResourceAttr(
						"qingcloud_eip.foo", "bandwidth", "2"),
					resource.TestCheckResourceAttr(
						"qingcloud_eip.foo", "billing_mode", "traffic"),
					resource.TestCheckResourceAttr(
						"qingcloud_eip.foo", "description", "first"),
					resource.TestCheckResourceAttr(
						"qingcloud_eip.foo", "name", "first_eip"),
					resource.TestCheckResourceAttr(
						"qingcloud_eip.foo", "need_icp", "0"),
				),
			},
			resource.TestStep{
				Config: testAccEIPConfigTwo,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckEIPExists(
						"qingcloud_eip.foo", &eip),
					resource.TestCheckResourceAttr(
						"qingcloud_eip.foo", "bandwidth", "4"),
					resource.TestCheckResourceAttr(
						"qingcloud_eip.foo", "billing_mode", "bandwidth"),
					resource.TestCheckResourceAttr(
						"qingcloud_eip.foo", "description", "eip"),
					resource.TestCheckResourceAttr(
						"qingcloud_eip.foo", "name", "eip"),
					resource.TestCheckResourceAttr(
						"qingcloud_eip.foo", "need_icp", "0"),
				),
			},
		},
	})

}

func testAccCheckEIPExists(n string, eip *qc.DescribeEIPsOutput) 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 fmt.Errorf("No EIP ID is set")
		}

		client := testAccProvider.Meta().(*QingCloudClient)
		input := new(qc.DescribeEIPsInput)
		input.EIPs = []*string{qc.String(rs.Primary.ID)}
		d, err := client.eip.DescribeEIPs(input)

		log.Printf("[WARN] eip id %#v", rs.Primary.ID)

		if err != nil {
			return err
		}

		if d == nil || qc.StringValue(d.EIPSet[0].EIPAddr) == "" {
			return fmt.Errorf("EIP not found")
		}

		*eip = *d
		return nil
	}
}
func testAccCheckEIPDestroy(s *terraform.State) error {
	return testAccCheckEIPDestroyWithProvider(s, testAccProvider)
}

func testAccCheckEIPDestroyWithProvider(s *terraform.State, provider *schema.Provider) error {
	client := provider.Meta().(*QingCloudClient)
	for _, rs := range s.RootModule().Resources {
		if rs.Type != "qingcloud_eip" {
			continue
		}

		// Try to find the resource
		input := new(qc.DescribeEIPsInput)
		input.EIPs = []*string{qc.String(rs.Primary.ID)}
		output, err := client.eip.DescribeEIPs(input)
		if err == nil && qc.IntValue(output.RetCode) == 0 {
			if len(output.EIPSet) != 0 && qc.StringValue(output.EIPSet[0].Status) != "released" {
				return fmt.Errorf("Found  EIP: %s", rs.Primary.ID)
			}
		}
	}
	return nil
}

const testAccEIPConfig = `
resource "qingcloud_eip" "foo" {
    name = "first_eip"
    description = "first"
    billing_mode = "traffic"
    bandwidth = 2
    need_icp = 0
} `
const testAccEIPConfigTwo = `
resource "qingcloud_eip" "foo" {
    name = "eip"
    description = "eip"
    billing_mode = "bandwidth"
    bandwidth = 4
    need_icp = 0
} `
<commit_msg>add eip_with_tag test<commit_after>package qingcloud

import (
	"fmt"
	"log"
	"testing"

	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
	"github.com/hashicorp/terraform/terraform"
	qc "github.com/yunify/qingcloud-sdk-go/service"
)

func TestAccQingcloudEIP_basic(t *testing.T) {
	var eip qc.DescribeEIPsOutput

	resource.Test(t, resource.TestCase{
		PreCheck: func() {
			testAccPreCheck(t)
		},

		// module name
		IDRefreshName: "qingcloud_eip.foo",
		Providers:     testAccProviders,
		CheckDestroy:  testAccCheckEIPDestroy,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccEIPConfig,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckEIPExists(
						"qingcloud_eip.foo", &eip),
					resource.TestCheckResourceAttr(
						"qingcloud_eip.foo", "bandwidth", "2"),
					resource.TestCheckResourceAttr(
						"qingcloud_eip.foo", "billing_mode", "traffic"),
					resource.TestCheckResourceAttr(
						"qingcloud_eip.foo", "description", "first"),
					resource.TestCheckResourceAttr(
						"qingcloud_eip.foo", "name", "first_eip"),
					resource.TestCheckResourceAttr(
						"qingcloud_eip.foo", "need_icp", "0"),
				),
			},
			resource.TestStep{
				Config: testAccEIPConfigTwo,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckEIPExists(
						"qingcloud_eip.foo", &eip),
					resource.TestCheckResourceAttr(
						"qingcloud_eip.foo", "bandwidth", "4"),
					resource.TestCheckResourceAttr(
						"qingcloud_eip.foo", "billing_mode", "bandwidth"),
					resource.TestCheckResourceAttr(
						"qingcloud_eip.foo", "description", "eip"),
					resource.TestCheckResourceAttr(
						"qingcloud_eip.foo", "name", "eip"),
					resource.TestCheckResourceAttr(
						"qingcloud_eip.foo", "need_icp", "0"),
				),
			},
		},
	})

}
func TestAccQingcloudEIP_tag(t *testing.T) {
	var eip qc.DescribeEIPsOutput

	testTagNameValue := func(names ...string) resource.TestCheckFunc {
		return func(state *terraform.State) error {
			tags := eip.EIPSet[0].Tags
			same_count := 0
			for _, tag := range tags {
				for _, name := range names {
					if qc.StringValue(tag.TagName) == name {
						same_count++
					}
					if same_count == len(eip.EIPSet[0].Tags) {
						return nil
					}
				}
			}
			return fmt.Errorf("tag name error %#v", names)
		}
	}
	testTagDetach := func() resource.TestCheckFunc {
		return func(state *terraform.State) error {
			if len(eip.EIPSet[0].Tags) != 0 {
				return fmt.Errorf("tag not detach ")
			}
			return nil
		}
	}

	resource.Test(t, resource.TestCase{
		PreCheck: func() {
			testAccPreCheck(t)
		},
		IDRefreshName: "qingcloud_eip.foo",
		Providers:     testAccProviders,
		CheckDestroy:  testAccCheckEIPDestroy,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccEipConfigTag,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckEIPExists(
						"qingcloud_eip.foo", &eip),
					testTagNameValue("11", "22"),
				),
			},
			resource.TestStep{
				Config: testAccEipConfigTagTwo,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckEIPExists(
						"qingcloud_eip.foo", &eip),
					testTagDetach(),
				),
			},
		},
	})

}

func testAccCheckEIPExists(n string, eip *qc.DescribeEIPsOutput) 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 fmt.Errorf("No EIP ID is set")
		}

		client := testAccProvider.Meta().(*QingCloudClient)
		input := new(qc.DescribeEIPsInput)
		input.EIPs = []*string{qc.String(rs.Primary.ID)}
		d, err := client.eip.DescribeEIPs(input)

		log.Printf("[WARN] eip id %#v", rs.Primary.ID)

		if err != nil {
			return err
		}

		if d == nil || qc.StringValue(d.EIPSet[0].EIPAddr) == "" {
			return fmt.Errorf("EIP not found")
		}

		*eip = *d
		return nil
	}
}
func testAccCheckEIPDestroy(s *terraform.State) error {
	return testAccCheckEIPDestroyWithProvider(s, testAccProvider)
}

func testAccCheckEIPDestroyWithProvider(s *terraform.State, provider *schema.Provider) error {
	client := provider.Meta().(*QingCloudClient)
	for _, rs := range s.RootModule().Resources {
		if rs.Type != "qingcloud_eip" {
			continue
		}

		// Try to find the resource
		input := new(qc.DescribeEIPsInput)
		input.EIPs = []*string{qc.String(rs.Primary.ID)}
		output, err := client.eip.DescribeEIPs(input)
		if err == nil && qc.IntValue(output.RetCode) == 0 {
			if len(output.EIPSet) != 0 && qc.StringValue(output.EIPSet[0].Status) != "released" {
				return fmt.Errorf("Found  EIP: %s", rs.Primary.ID)
			}
		}
	}
	return nil
}

const testAccEIPConfig = `
resource "qingcloud_eip" "foo" {
    name = "first_eip"
    description = "first"
    billing_mode = "traffic"
    bandwidth = 2
    need_icp = 0
} `
const testAccEIPConfigTwo = `
resource "qingcloud_eip" "foo" {
    name = "eip"
    description = "eip"
    billing_mode = "bandwidth"
    bandwidth = 4
    need_icp = 0
} `

const testAccEipConfigTag = `

resource "qingcloud_eip" "foo" {
    name = "eip"
    description = "eip"
    billing_mode = "bandwidth"
    bandwidth = 4
    need_icp = 0
	tag_ids = ["${qingcloud_tag.test.id}",
				"${qingcloud_tag.test2.id}"]
}
resource "qingcloud_tag" "test"{
	name="11"
}
resource "qingcloud_tag" "test2"{
	name="22"
}
`
const testAccEipConfigTagTwo = `

resource "qingcloud_eip" "foo" {
    name = "eip"
    description = "eip"
    billing_mode = "bandwidth"
    bandwidth = 4
    need_icp = 0
}
resource "qingcloud_tag" "test"{
	name="11"
}
resource "qingcloud_tag" "test2"{
	name="22"
}
`
<|endoftext|>


<commit_before>// Copyright 2016 Keybase Inc. All rights reserved.
// Use of this source code is governed by a BSD
// license that can be found in the LICENSE file.

package libkbfs

import (
	"crypto/rand"
	"errors"
	"sync"
	"testing"

	"github.com/keybase/kbfs/kbfscodec"
	"github.com/keybase/kbfs/kbfscrypto"
	"github.com/stretchr/testify/require"
	"golang.org/x/net/context"
)

// blockReturner contains a block value to copy into requested blocks, and a
// channel to synchronize on with the worker.
type blockReturner struct {
	block      Block
	continueCh chan error
	startCh    chan struct{}
}

// fakeBlockGetter allows specifying and obtaining fake blocks.
type fakeBlockGetter struct {
	mtx           sync.RWMutex
	blockMap      map[BlockPointer]blockReturner
	codec         kbfscodec.Codec
	respectCancel bool
}

// newFakeBlockGetter returns a fakeBlockGetter.
func newFakeBlockGetter(respectCancel bool) *fakeBlockGetter {
	return &fakeBlockGetter{
		blockMap:      make(map[BlockPointer]blockReturner),
		codec:         kbfscodec.NewMsgpack(),
		respectCancel: respectCancel,
	}
}

// setBlockToReturn sets the block that will be returned for a given
// BlockPointer. Returns a writeable channel that getBlock will wait on, to
// allow synchronization of tests.
func (bg *fakeBlockGetter) setBlockToReturn(blockPtr BlockPointer, block Block) (startCh <-chan struct{}, continueCh chan<- error) {
	bg.mtx.Lock()
	defer bg.mtx.Unlock()
	sCh, cCh := make(chan struct{}), make(chan error)
	bg.blockMap[blockPtr] = blockReturner{
		block:      block,
		startCh:    sCh,
		continueCh: cCh,
	}
	return sCh, cCh
}

// getBlock implements the interface for realBlockGetter.
func (bg *fakeBlockGetter) getBlock(ctx context.Context, kmd KeyMetadata, blockPtr BlockPointer, block Block) error {
	bg.mtx.RLock()
	defer bg.mtx.RUnlock()
	source, ok := bg.blockMap[blockPtr]
	if !ok {
		return errors.New("Block doesn't exist in fake block map")
	}
	cancelCh := make(chan struct{})
	if bg.respectCancel {
		go func() {
			<-ctx.Done()
			close(cancelCh)
		}()
	}
	// Wait until the caller tells us to continue
	for {
		select {
		case source.startCh <- struct{}{}:
		case err := <-source.continueCh:
			if err != nil {
				return err
			}
			return kbfscodec.Update(bg.codec, block, source.block)
		case <-cancelCh:
			return ctx.Err()
		}
	}
}

func (bg *fakeBlockGetter) assembleBlock(ctx context.Context,
	kmd KeyMetadata, ptr BlockPointer, block Block, buf []byte,
	serverHalf kbfscrypto.BlockCryptKeyServerHalf) error {
	source, ok := bg.blockMap[ptr]
	if !ok {
		return errors.New("Block doesn't exist in fake block map")
	}
	block.Set(source.block)
	return nil
}

func makeFakeFileBlock(t *testing.T, doHash bool) *FileBlock {
	buf := make([]byte, 16)
	_, err := rand.Read(buf)
	require.NoError(t, err)
	block := &FileBlock{
		Contents: buf,
	}
	if doHash {
		_ = block.GetHash()
	}
	return block
}

func TestBlockRetrievalWorkerBasic(t *testing.T) {
	t.Log("Test the basic ability of a worker to return a block.")
	bg := newFakeBlockGetter(false)
	q := newBlockRetrievalQueue(1, newTestBlockRetrievalConfig(t, bg, nil))
	require.NotNil(t, q)
	defer q.Shutdown()

	w := newBlockRetrievalWorker(bg, q)
	require.NotNil(t, w)
	defer w.Shutdown()

	ptr1 := makeRandomBlockPointer(t)
	block1 := makeFakeFileBlock(t, false)
	_, continueCh1 := bg.setBlockToReturn(ptr1, block1)

	block := &FileBlock{}
	ch := q.Request(context.Background(), 1, makeKMD(), ptr1, block, NoCacheEntry)
	continueCh1 <- nil
	err := <-ch
	require.NoError(t, err)
	require.Equal(t, block1, block)
}

func TestBlockRetrievalWorkerMultipleWorkers(t *testing.T) {
	t.Log("Test the ability of multiple workers to retrieve concurrently.")
	bg := newFakeBlockGetter(false)
	q := newBlockRetrievalQueue(2, newTestBlockRetrievalConfig(t, bg, nil))
	require.NotNil(t, q)
	defer q.Shutdown()

	ptr1, ptr2 := makeRandomBlockPointer(t), makeRandomBlockPointer(t)
	block1, block2 := makeFakeFileBlock(t, false), makeFakeFileBlock(t, false)
	_, continueCh1 := bg.setBlockToReturn(ptr1, block1)
	_, continueCh2 := bg.setBlockToReturn(ptr2, block2)

	t.Log("Make 2 requests for 2 different blocks")
	block := &FileBlock{}
	req1Ch := q.Request(context.Background(), 1, makeKMD(), ptr1, block, NoCacheEntry)
	req2Ch := q.Request(context.Background(), 1, makeKMD(), ptr2, block, NoCacheEntry)

	t.Log("Allow the second request to complete before the first")
	continueCh2 <- nil
	err := <-req2Ch
	require.NoError(t, err)
	require.Equal(t, block2, block)

	t.Log("Make another request for ptr2")
	req2Ch = q.Request(context.Background(), 1, makeKMD(), ptr2, block, NoCacheEntry)
	continueCh2 <- nil
	err = <-req2Ch
	require.NoError(t, err)
	require.Equal(t, block2, block)

	t.Log("Complete the ptr1 request")
	continueCh1 <- nil
	err = <-req1Ch
	require.NoError(t, err)
	require.Equal(t, block1, block)
}

func TestBlockRetrievalWorkerWithQueue(t *testing.T) {
	t.Log("Test the ability of a worker and queue to work correctly together.")
	bg := newFakeBlockGetter(false)
	q := newBlockRetrievalQueue(1, newTestBlockRetrievalConfig(t, bg, nil))
	require.NotNil(t, q)
	defer q.Shutdown()

	ptr1, ptr2, ptr3 := makeRandomBlockPointer(t), makeRandomBlockPointer(t), makeRandomBlockPointer(t)
	block1, block2, block3 := makeFakeFileBlock(t, false), makeFakeFileBlock(t, false), makeFakeFileBlock(t, false)
	startCh1, continueCh1 := bg.setBlockToReturn(ptr1, block1)
	_, continueCh2 := bg.setBlockToReturn(ptr2, block2)
	_, continueCh3 := bg.setBlockToReturn(ptr3, block3)

	t.Log("Make 3 retrievals for 3 different blocks. All retrievals after the first should be queued.")
	block := &FileBlock{}
	testBlock1 := &FileBlock{}
	testBlock2 := &FileBlock{}
	req1Ch := q.Request(context.Background(), 1, makeKMD(), ptr1, block, NoCacheEntry)
	req2Ch := q.Request(context.Background(), 1, makeKMD(), ptr2, block, NoCacheEntry)
	req3Ch := q.Request(context.Background(), 1, makeKMD(), ptr3, testBlock1, NoCacheEntry)
	// Ensure the worker picks up the first request
	<-startCh1
	t.Log("Make a high priority request for the third block, which should complete next.")
	req4Ch := q.Request(context.Background(), 2, makeKMD(), ptr3, testBlock2, NoCacheEntry)

	t.Log("Allow the ptr1 retrieval to complete.")
	continueCh1 <- nil
	err := <-req1Ch
	require.NoError(t, err)
	require.Equal(t, block1, block)

	t.Log("Allow the ptr3 retrieval to complete. Both waiting requests should complete.")
	continueCh3 <- nil
	err1 := <-req3Ch
	err2 := <-req4Ch
	require.NoError(t, err1)
	require.NoError(t, err2)
	require.Equal(t, block3, testBlock1)
	require.Equal(t, block3, testBlock2)

	t.Log("Complete the ptr2 retrieval.")
	continueCh2 <- nil
	err = <-req2Ch
	require.NoError(t, err)
	require.Equal(t, block2, block)
}

func TestBlockRetrievalWorkerCancel(t *testing.T) {
	t.Log("Test the ability of a worker to handle a request cancelation.")
	bg := newFakeBlockGetter(true)
	q := newBlockRetrievalQueue(1, newTestBlockRetrievalConfig(t, bg, nil))
	require.NotNil(t, q)
	defer q.Shutdown()

	w := newBlockRetrievalWorker(bg, q)
	require.NotNil(t, w)
	defer w.Shutdown()

	ptr1 := makeRandomBlockPointer(t)
	block1 := makeFakeFileBlock(t, false)
	_, _ = bg.setBlockToReturn(ptr1, block1)

	block := &FileBlock{}
	ctx, cancel := context.WithCancel(context.Background())
	cancel()
	ch := q.Request(ctx, 1, makeKMD(), ptr1, block, NoCacheEntry)
	err := <-ch
	require.EqualError(t, err, context.Canceled.Error())
}

func TestBlockRetrievalWorkerShutdown(t *testing.T) {
	t.Log("Test that worker shutdown works.")
	bg := newFakeBlockGetter(false)
	q := newBlockRetrievalQueue(0, newTestBlockRetrievalConfig(t, bg, nil))
	require.NotNil(t, q)
	defer q.Shutdown()

	w := newBlockRetrievalWorker(bg, q)
	require.NotNil(t, w)

	ptr1 := makeRandomBlockPointer(t)
	block1 := makeFakeFileBlock(t, false)
	_, continueCh := bg.setBlockToReturn(ptr1, block1)

	w.Shutdown()
	block := &FileBlock{}
	ctx, cancel := context.WithCancel(context.Background())
	// Ensure the context loop is stopped so the test doesn't leak goroutines
	defer cancel()
	ch := q.Request(ctx, 1, makeKMD(), ptr1, block, NoCacheEntry)
	shutdown := false
	select {
	case <-ch:
		t.Fatal("Expected not to retrieve a result from the Request.")
	case continueCh <- nil:
		t.Fatal("Expected the block getter not to be receiving.")
	default:
		shutdown = true
	}
	require.True(t, shutdown)
	w.Shutdown()
	require.True(t, shutdown)
}

func TestBlockRetrievalWorkerMultipleBlockTypes(t *testing.T) {
	t.Log("Test that we can retrieve the same block into different block types.")
	codec := kbfscodec.NewMsgpack()
	bg := newFakeBlockGetter(false)
	q := newBlockRetrievalQueue(1, newTestBlockRetrievalConfig(t, bg, nil))
	require.NotNil(t, q)
	defer q.Shutdown()

	t.Log("Setup source blocks")
	ptr1 := makeRandomBlockPointer(t)
	block1 := makeFakeFileBlock(t, false)
	_, continueCh1 := bg.setBlockToReturn(ptr1, block1)
	testCommonBlock := &CommonBlock{}
	err := kbfscodec.Update(codec, testCommonBlock, block1)
	require.NoError(t, err)

	t.Log("Make a retrieval for the same block twice, but with a different target block type.")
	testBlock1 := &FileBlock{}
	testBlock2 := &CommonBlock{}
	req1Ch := q.Request(context.Background(), 1, makeKMD(), ptr1, testBlock1, NoCacheEntry)
	req2Ch := q.Request(context.Background(), 1, makeKMD(), ptr1, testBlock2, NoCacheEntry)

	t.Log("Allow the first ptr1 retrieval to complete.")
	continueCh1 <- nil
	err = <-req1Ch
	require.NoError(t, err)
	require.Equal(t, testBlock1, block1)

	t.Log("Allow the second ptr1 retrieval to complete.")
	continueCh1 <- nil
	err = <-req2Ch
	require.NoError(t, err)
	require.Equal(t, testBlock2, testCommonBlock)
}
<commit_msg>block_retrieval_worker_test: Added correct check at the end of Shutdown test.<commit_after>// Copyright 2016 Keybase Inc. All rights reserved.
// Use of this source code is governed by a BSD
// license that can be found in the LICENSE file.
package libkbfs

import (
	"crypto/rand"
	"errors"
	"sync"
	"testing"
	"time"

	"github.com/keybase/kbfs/kbfscodec"
	"github.com/keybase/kbfs/kbfscrypto"
	"github.com/stretchr/testify/require"
	"golang.org/x/net/context"
)

// blockReturner contains a block value to copy into requested blocks, and a
// channel to synchronize on with the worker.
type blockReturner struct {
	block      Block
	continueCh chan error
	startCh    chan struct{}
}

// fakeBlockGetter allows specifying and obtaining fake blocks.
type fakeBlockGetter struct {
	mtx           sync.RWMutex
	blockMap      map[BlockPointer]blockReturner
	codec         kbfscodec.Codec
	respectCancel bool
}

// newFakeBlockGetter returns a fakeBlockGetter.
func newFakeBlockGetter(respectCancel bool) *fakeBlockGetter {
	return &fakeBlockGetter{
		blockMap:      make(map[BlockPointer]blockReturner),
		codec:         kbfscodec.NewMsgpack(),
		respectCancel: respectCancel,
	}
}

// setBlockToReturn sets the block that will be returned for a given
// BlockPointer. Returns a writeable channel that getBlock will wait on, to
// allow synchronization of tests.
func (bg *fakeBlockGetter) setBlockToReturn(blockPtr BlockPointer, block Block) (startCh <-chan struct{}, continueCh chan<- error) {
	bg.mtx.Lock()
	defer bg.mtx.Unlock()
	sCh, cCh := make(chan struct{}), make(chan error)
	bg.blockMap[blockPtr] = blockReturner{
		block:      block,
		startCh:    sCh,
		continueCh: cCh,
	}
	return sCh, cCh
}

// getBlock implements the interface for realBlockGetter.
func (bg *fakeBlockGetter) getBlock(ctx context.Context, kmd KeyMetadata, blockPtr BlockPointer, block Block) error {
	bg.mtx.RLock()
	defer bg.mtx.RUnlock()
	source, ok := bg.blockMap[blockPtr]
	if !ok {
		return errors.New("Block doesn't exist in fake block map")
	}
	cancelCh := make(chan struct{})
	if bg.respectCancel {
		go func() {
			<-ctx.Done()
			close(cancelCh)
		}()
	}
	// Wait until the caller tells us to continue
	for {
		select {
		case source.startCh <- struct{}{}:
		case err := <-source.continueCh:
			if err != nil {
				return err
			}
			return kbfscodec.Update(bg.codec, block, source.block)
		case <-cancelCh:
			return ctx.Err()
		}
	}
}

func (bg *fakeBlockGetter) assembleBlock(ctx context.Context,
	kmd KeyMetadata, ptr BlockPointer, block Block, buf []byte,
	serverHalf kbfscrypto.BlockCryptKeyServerHalf) error {
	source, ok := bg.blockMap[ptr]
	if !ok {
		return errors.New("Block doesn't exist in fake block map")
	}
	block.Set(source.block)
	return nil
}

func makeFakeFileBlock(t *testing.T, doHash bool) *FileBlock {
	buf := make([]byte, 16)
	_, err := rand.Read(buf)
	require.NoError(t, err)
	block := &FileBlock{
		Contents: buf,
	}
	if doHash {
		_ = block.GetHash()
	}
	return block
}

func TestBlockRetrievalWorkerBasic(t *testing.T) {
	t.Log("Test the basic ability of a worker to return a block.")
	bg := newFakeBlockGetter(false)
	q := newBlockRetrievalQueue(1, newTestBlockRetrievalConfig(t, bg, nil))
	require.NotNil(t, q)
	defer q.Shutdown()

	w := newBlockRetrievalWorker(bg, q)
	require.NotNil(t, w)
	defer w.Shutdown()

	ptr1 := makeRandomBlockPointer(t)
	block1 := makeFakeFileBlock(t, false)
	_, continueCh1 := bg.setBlockToReturn(ptr1, block1)

	block := &FileBlock{}
	ch := q.Request(context.Background(), 1, makeKMD(), ptr1, block, NoCacheEntry)
	continueCh1 <- nil
	err := <-ch
	require.NoError(t, err)
	require.Equal(t, block1, block)
}

func TestBlockRetrievalWorkerMultipleWorkers(t *testing.T) {
	t.Log("Test the ability of multiple workers to retrieve concurrently.")
	bg := newFakeBlockGetter(false)
	q := newBlockRetrievalQueue(2, newTestBlockRetrievalConfig(t, bg, nil))
	require.NotNil(t, q)
	defer q.Shutdown()

	ptr1, ptr2 := makeRandomBlockPointer(t), makeRandomBlockPointer(t)
	block1, block2 := makeFakeFileBlock(t, false), makeFakeFileBlock(t, false)
	_, continueCh1 := bg.setBlockToReturn(ptr1, block1)
	_, continueCh2 := bg.setBlockToReturn(ptr2, block2)

	t.Log("Make 2 requests for 2 different blocks")
	block := &FileBlock{}
	req1Ch := q.Request(context.Background(), 1, makeKMD(), ptr1, block, NoCacheEntry)
	req2Ch := q.Request(context.Background(), 1, makeKMD(), ptr2, block, NoCacheEntry)

	t.Log("Allow the second request to complete before the first")
	continueCh2 <- nil
	err := <-req2Ch
	require.NoError(t, err)
	require.Equal(t, block2, block)

	t.Log("Make another request for ptr2")
	req2Ch = q.Request(context.Background(), 1, makeKMD(), ptr2, block, NoCacheEntry)
	continueCh2 <- nil
	err = <-req2Ch
	require.NoError(t, err)
	require.Equal(t, block2, block)

	t.Log("Complete the ptr1 request")
	continueCh1 <- nil
	err = <-req1Ch
	require.NoError(t, err)
	require.Equal(t, block1, block)
}

func TestBlockRetrievalWorkerWithQueue(t *testing.T) {
	t.Log("Test the ability of a worker and queue to work correctly together.")
	bg := newFakeBlockGetter(false)
	q := newBlockRetrievalQueue(1, newTestBlockRetrievalConfig(t, bg, nil))
	require.NotNil(t, q)
	defer q.Shutdown()

	ptr1, ptr2, ptr3 := makeRandomBlockPointer(t), makeRandomBlockPointer(t), makeRandomBlockPointer(t)
	block1, block2, block3 := makeFakeFileBlock(t, false), makeFakeFileBlock(t, false), makeFakeFileBlock(t, false)
	startCh1, continueCh1 := bg.setBlockToReturn(ptr1, block1)
	_, continueCh2 := bg.setBlockToReturn(ptr2, block2)
	_, continueCh3 := bg.setBlockToReturn(ptr3, block3)

	t.Log("Make 3 retrievals for 3 different blocks. All retrievals after the first should be queued.")
	block := &FileBlock{}
	testBlock1 := &FileBlock{}
	testBlock2 := &FileBlock{}
	req1Ch := q.Request(context.Background(), 1, makeKMD(), ptr1, block, NoCacheEntry)
	req2Ch := q.Request(context.Background(), 1, makeKMD(), ptr2, block, NoCacheEntry)
	req3Ch := q.Request(context.Background(), 1, makeKMD(), ptr3, testBlock1, NoCacheEntry)
	// Ensure the worker picks up the first request
	<-startCh1
	t.Log("Make a high priority request for the third block, which should complete next.")
	req4Ch := q.Request(context.Background(), 2, makeKMD(), ptr3, testBlock2, NoCacheEntry)

	t.Log("Allow the ptr1 retrieval to complete.")
	continueCh1 <- nil
	err := <-req1Ch
	require.NoError(t, err)
	require.Equal(t, block1, block)

	t.Log("Allow the ptr3 retrieval to complete. Both waiting requests should complete.")
	continueCh3 <- nil
	err1 := <-req3Ch
	err2 := <-req4Ch
	require.NoError(t, err1)
	require.NoError(t, err2)
	require.Equal(t, block3, testBlock1)
	require.Equal(t, block3, testBlock2)

	t.Log("Complete the ptr2 retrieval.")
	continueCh2 <- nil
	err = <-req2Ch
	require.NoError(t, err)
	require.Equal(t, block2, block)
}

func TestBlockRetrievalWorkerCancel(t *testing.T) {
	t.Log("Test the ability of a worker to handle a request cancelation.")
	bg := newFakeBlockGetter(true)
	q := newBlockRetrievalQueue(1, newTestBlockRetrievalConfig(t, bg, nil))
	require.NotNil(t, q)
	defer q.Shutdown()

	w := newBlockRetrievalWorker(bg, q)
	require.NotNil(t, w)
	defer w.Shutdown()

	ptr1 := makeRandomBlockPointer(t)
	block1 := makeFakeFileBlock(t, false)
	_, _ = bg.setBlockToReturn(ptr1, block1)

	block := &FileBlock{}
	ctx, cancel := context.WithCancel(context.Background())
	cancel()
	ch := q.Request(ctx, 1, makeKMD(), ptr1, block, NoCacheEntry)
	err := <-ch
	require.EqualError(t, err, context.Canceled.Error())
}

func TestBlockRetrievalWorkerShutdown(t *testing.T) {
	t.Log("Test that worker shutdown works.")
	bg := newFakeBlockGetter(false)
	q := newBlockRetrievalQueue(0, newTestBlockRetrievalConfig(t, bg, nil))
	require.NotNil(t, q)
	defer q.Shutdown()

	w := newBlockRetrievalWorker(bg, q)
	require.NotNil(t, w)

	ptr1 := makeRandomBlockPointer(t)
	block1 := makeFakeFileBlock(t, false)
	_, continueCh := bg.setBlockToReturn(ptr1, block1)

	w.Shutdown()
	block := &FileBlock{}
	ctx, cancel := context.WithCancel(context.Background())
	// Ensure the context loop is stopped so the test doesn't leak goroutines
	defer cancel()
	ch := q.Request(ctx, 1, makeKMD(), ptr1, block, NoCacheEntry)
	shutdown := false
	select {
	case <-ch:
		t.Fatal("Expected not to retrieve a result from the Request.")
	case continueCh <- nil:
		t.Fatal("Expected the block getter not to be receiving.")
	default:
		shutdown = true
	}
	require.True(t, shutdown)

	// Ensure the test completes in a reasonable time.
	timer := time.NewTimer(10 * time.Second)
	doneCh := make(chan struct{})
	go func() {
		w.Shutdown()
		close(doneCh)
	}()
	select {
	case <-timer.C:
		t.Fatal("Expected another Shutdown not to block.")
	case <-doneCh:
	}
}

func TestBlockRetrievalWorkerMultipleBlockTypes(t *testing.T) {
	t.Log("Test that we can retrieve the same block into different block types.")
	codec := kbfscodec.NewMsgpack()
	bg := newFakeBlockGetter(false)
	q := newBlockRetrievalQueue(1, newTestBlockRetrievalConfig(t, bg, nil))
	require.NotNil(t, q)
	defer q.Shutdown()

	t.Log("Setup source blocks")
	ptr1 := makeRandomBlockPointer(t)
	block1 := makeFakeFileBlock(t, false)
	_, continueCh1 := bg.setBlockToReturn(ptr1, block1)
	testCommonBlock := &CommonBlock{}
	err := kbfscodec.Update(codec, testCommonBlock, block1)
	require.NoError(t, err)

	t.Log("Make a retrieval for the same block twice, but with a different target block type.")
	testBlock1 := &FileBlock{}
	testBlock2 := &CommonBlock{}
	req1Ch := q.Request(context.Background(), 1, makeKMD(), ptr1, testBlock1, NoCacheEntry)
	req2Ch := q.Request(context.Background(), 1, makeKMD(), ptr1, testBlock2, NoCacheEntry)

	t.Log("Allow the first ptr1 retrieval to complete.")
	continueCh1 <- nil
	err = <-req1Ch
	require.NoError(t, err)
	require.Equal(t, testBlock1, block1)

	t.Log("Allow the second ptr1 retrieval to complete.")
	continueCh1 <- nil
	err = <-req2Ch
	require.NoError(t, err)
	require.Equal(t, testBlock2, testCommonBlock)
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"time"
	"strings"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceContainerReplicaController() *schema.Resource {
	return &schema.Resource{
		Create: resourceContainerReplicaControllerCreate,
		Read:   resourceContainerReplicaControllerRead,
		Delete: resourceContainerReplicaControllerDelete,

		Schema: map[string]*schema.Schema{
			"name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"docker_image": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"container_name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"zone": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"external_port": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: true,
			},

			"resource_version": &schema.Schema{
				Type:     schema.TypeList,
				ForceNew: true,
				Optional: true,
				Elem: &schema.Schema{
					Type: schema.TypeString,
				},
			},

			"optional_args": &schema.Schema{
				Type:     schema.TypeMap,
				Optional: true,
				ForceNew: true,
				Elem:	  schema.TypeString,
			},

			"env_args": &schema.Schema{
				Type:     schema.TypeMap,
				Optional: true,
				ForceNew: true,
				Elem:	  schema.TypeString,
			},

			"external_ip": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

		},
	}
}

func resourceContainerReplicaControllerCreate(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)
	err := config.initKubectl(d.Get("container_name").(string), d.Get("zone").(string))
	if err != nil {
		return err
	}

	optional_args := cleanAdditionalArgs(d.Get("optional_args").(map[string]interface{}))
	env_args := cleanAdditionalArgs(d.Get("env_args").(map[string]interface{}))
	uid, err := CreateKubeRC(d.Get("name").(string), d.Get("docker_image").(string), d.Get("external_port").(string), optional_args, env_args)
	if err != nil {
		return err
	}

	err = resourceContainerReplicaControllerRead(d, meta)
	if err != nil {
		return err
	}

	d.SetId(uid)

	return nil
}

// if the error string has a 'code=404' in it, the owning cluster is gone.
//  remove the rc from the tfstate file
func checkMissingCluster(d *schema.ResourceData, err error) error {
	if strings.Contains(err.Error(), "code=404") {
		//  the owning cluster doesn't exist, the container can't
		d.SetId("")
		return nil
	}
	return err
}

func resourceContainerReplicaControllerRead(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)
	err := config.initKubectl(d.Get("container_name").(string), d.Get("zone").(string))
	if err != nil {
		return checkMissingCluster(d, err)
	}

    //  the endpoint kubectl hits is flaky.  put a loop on it.
	pod_count, external_ip, err := ReadKubeRC(d.Get("name").(string), d.Get("external_port").(string))
	if err != nil {
		is_error := true
		for i := 0; i < (10 * 6) && is_error; i++ {
			time.Sleep(10 * time.Second)
			pod_count, external_ip, err = ReadKubeRC(d.Get("name").(string), d.Get("external_port").(string))
			if err == nil {
				is_error = false
			}
			log.Println("Waiting for a non-error response from the kubectl API...")
		}
		if err != nil {
			return err
		}
	}

	if pod_count == 0 {
		//  something has gone awry, there should always be at least one pod
		log.Printf("There are no pods associated with this Replica Controller.  This is unexpected and probably wrong.  Please investigate")
	}

	if external_ip != "" {
		d.Set("external_ip", external_ip)
	}

	return nil
}

func resourceContainerReplicaControllerDelete(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)
	err := config.initKubectl(d.Get("container_name").(string), d.Get("zone").(string))
	if err != nil {
		return checkMissingCluster(d, err)
	}

	err = DeleteKubeRC(d.Get("name").(string),d.Get("external_port").(string))
	if err != nil {
		return err
	}

	d.SetId("")
	return nil
}
<commit_msg>5 not 10 minutes<commit_after>package main

import (
	"log"
	"time"
	"strings"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceContainerReplicaController() *schema.Resource {
	return &schema.Resource{
		Create: resourceContainerReplicaControllerCreate,
		Read:   resourceContainerReplicaControllerRead,
		Delete: resourceContainerReplicaControllerDelete,

		Schema: map[string]*schema.Schema{
			"name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"docker_image": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"container_name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"zone": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"external_port": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: true,
			},

			"resource_version": &schema.Schema{
				Type:     schema.TypeList,
				ForceNew: true,
				Optional: true,
				Elem: &schema.Schema{
					Type: schema.TypeString,
				},
			},

			"optional_args": &schema.Schema{
				Type:     schema.TypeMap,
				Optional: true,
				ForceNew: true,
				Elem:	  schema.TypeString,
			},

			"env_args": &schema.Schema{
				Type:     schema.TypeMap,
				Optional: true,
				ForceNew: true,
				Elem:	  schema.TypeString,
			},

			"external_ip": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

		},
	}
}

func resourceContainerReplicaControllerCreate(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)
	err := config.initKubectl(d.Get("container_name").(string), d.Get("zone").(string))
	if err != nil {
		return err
	}

	optional_args := cleanAdditionalArgs(d.Get("optional_args").(map[string]interface{}))
	env_args := cleanAdditionalArgs(d.Get("env_args").(map[string]interface{}))
	uid, err := CreateKubeRC(d.Get("name").(string), d.Get("docker_image").(string), d.Get("external_port").(string), optional_args, env_args)
	if err != nil {
		return err
	}

	err = resourceContainerReplicaControllerRead(d, meta)
	if err != nil {
		return err
	}

	d.SetId(uid)

	return nil
}

// if the error string has a 'code=404' in it, the owning cluster is gone.
//  remove the rc from the tfstate file
func checkMissingCluster(d *schema.ResourceData, err error) error {
	if strings.Contains(err.Error(), "code=404") {
		//  the owning cluster doesn't exist, the container can't
		d.SetId("")
		return nil
	}
	return err
}

func resourceContainerReplicaControllerRead(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)
	err := config.initKubectl(d.Get("container_name").(string), d.Get("zone").(string))
	if err != nil {
		return checkMissingCluster(d, err)
	}

    //  the endpoint kubectl hits is flaky.  put a loop on it.  5 minutes
	pod_count, external_ip, err := ReadKubeRC(d.Get("name").(string), d.Get("external_port").(string))
	if err != nil {
		is_error := true
		for i := 0; i < (5 * 6) && is_error; i++ {
			time.Sleep(10 * time.Second)
			pod_count, external_ip, err = ReadKubeRC(d.Get("name").(string), d.Get("external_port").(string))
			if err == nil {
				is_error = false
			}
			log.Println("Waiting for a non-error response from the kubectl API...")
		}
		if err != nil {
			return err
		}
	}

	if pod_count == 0 {
		//  something has gone awry, there should always be at least one pod
		log.Printf("There are no pods associated with this Replica Controller.  This is unexpected and probably wrong.  Please investigate")
	}

	if external_ip != "" {
		d.Set("external_ip", external_ip)
	}

	return nil
}

func resourceContainerReplicaControllerDelete(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)
	err := config.initKubectl(d.Get("container_name").(string), d.Get("zone").(string))
	if err != nil {
		return checkMissingCluster(d, err)
	}

	err = DeleteKubeRC(d.Get("name").(string),d.Get("external_port").(string))
	if err != nil {
		return err
	}

	d.SetId("")
	return nil
}
<|endoftext|>


<commit_before>package mpawsec2

import (
	"errors"
	"flag"
	"fmt"
	"log"
	"os"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/ec2metadata"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/cloudwatch"

	mp "github.com/mackerelio/go-mackerel-plugin"
)

// EC2Plugin is a mackerel plugin for ec2
type EC2Plugin struct {
	InstanceID  string
	Region      string
	Credentials *credentials.Credentials
	CloudWatch  *cloudwatch.CloudWatch
}

var graphdef = map[string]mp.Graphs{
	"ec2.CPUUtilization": {
		Label: "CPU Utilization",
		Unit:  "percentage",
		Metrics: []mp.Metrics{
			{Name: "CPUUtilization", Label: "CPUUtilization"},
		},
	},
	"ec2.DiskBytes": {
		Label: "Disk Bytes",
		Unit:  "bytes",
		Metrics: []mp.Metrics{
			{Name: "DiskReadBytes", Label: "DiskReadBytes"},
			{Name: "DiskWriteBytes", Label: "DiskWriteBytes"},
		},
	},
	"ec2.DiskOps": {
		Label: "Disk Ops",
		Unit:  "float",
		Metrics: []mp.Metrics{
			{Name: "DiskReadOps", Label: "DiskReadOps"},
			{Name: "DiskWriteOps", Label: "DiskWriteOps"},
		},
	},
	"ec2.Network": {
		Label: "Network",
		Unit:  "bytes",
		Metrics: []mp.Metrics{
			{Name: "NetworkIn", Label: "NetworkIn"},
			{Name: "NetworkOut", Label: "NetworkOut"},
		},
	},
	"ec2.NetworkPackets": {
		Label: "Network",
		Unit:  "bytes",
		Metrics: []mp.Metrics{
			{Name: "NetworkPacketsIn", Label: "NetworkPacketsIn"},
			{Name: "NetworkPacketsOut", Label: "NetworkPacketsOut"},
		},
	},
	"ec2.StatusCheckFailed": {
		Label: "StatusCheckFailed",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: "StatusCheckFailed", Label: "StatusCheckFailed"},
			{Name: "StatusCheckFailed_Instance", Label: "StatusCheckFailed_Instance"},
			{Name: "StatusCheckFailed_System", Label: "StatusCheckFailed_System"},
		},
	},
}

// GraphDefinition returns graphdef
func (p EC2Plugin) GraphDefinition() map[string]mp.Graphs {
	return graphdef
}

func getLastPoint(cloudWatch *cloudwatch.CloudWatch, dimension *cloudwatch.Dimension, metricName string) (float64, error) {
	now := time.Now()
	response, err := cloudWatch.GetMetricStatistics(&cloudwatch.GetMetricStatisticsInput{
		StartTime:  aws.Time(now.Add(time.Duration(600) * time.Second * -1)),
		EndTime:    aws.Time(now),
		Period:     aws.Int64(60),
		Namespace:  aws.String("AWS/EC2"),
		Dimensions: []*cloudwatch.Dimension{dimension},
		MetricName: aws.String(metricName),
		Statistics: []*string{aws.String("Average")},
	})
	if err != nil {
		return 0, err
	}

	datapoints := response.Datapoints
	if len(datapoints) == 0 {
		return 0, errors.New("fetched no datapoints")
	}

	latest := time.Unix(0, 0)
	var latestVal float64
	for _, dp := range datapoints {
		if dp.Timestamp.Before(latest) {
			continue
		}

		latest = *dp.Timestamp
		latestVal = *dp.Average
	}

	return latestVal, nil
}

// FetchMetrics fetches metrics from CloudWatch
func (p EC2Plugin) FetchMetrics() (map[string]float64, error) {
	stat := make(map[string]float64)
	p.CloudWatch = cloudwatch.New(session.New(
		&aws.Config{
			Credentials: p.Credentials,
			Region:      &p.Region,
		}))
	dimension := &cloudwatch.Dimension{
		Name:  aws.String("InstanceId"),
		Value: aws.String(p.InstanceID),
	}

	for _, met := range [...]string{
		"CPUUtilization",
		"DiskReadBytes",
		"DiskReadOps",
		"DiskWriteBytes",
		"DiskWriteOps",
		"NetworkIn",
		"NetworkOut",
		"NetworkPacketsIn",
		"NetworkPacketsOut",
		"StatusCheckFailed",
		"StatusCheckFailed_Instance",
		"StatusCheckFailed_System",
	} {
		v, err := getLastPoint(p.CloudWatch, dimension, met)
		if err == nil {
			stat[met] = v
		} else {
			log.Printf("%s: %s", met, err)
		}
	}

	return stat, nil
}

// Do the plugin
func Do() {
	optAccessKeyID := flag.String("access-key-id", "", "AWS Access Key ID")
	optSecretAccessKey := flag.String("secret-access-key", "", "AWS Secret Access Key")
	optRegion := flag.String("region", "", "AWS Region")
	optInstanceID := flag.String("instance-id", "", "Instance ID")
	optTempfile := flag.String("tempfile", "", "Temp file name")
	flag.Parse()

	var ec2 EC2Plugin

	// use credentials from option
	if *optAccessKeyID != "" && *optSecretAccessKey != "" {
		ec2.Credentials = credentials.NewStaticCredentials(*optAccessKeyID, *optSecretAccessKey, "")
	}

	// get metadata in ec2 instance
	ec2MC := ec2metadata.New(session.New())
	ec2.Region = *optRegion
	if *optRegion == "" {
		ec2.Region, _ = ec2MC.Region()
	}
	ec2.InstanceID = *optInstanceID
	if *optInstanceID == "" {
		ec2.InstanceID, _ = ec2MC.GetMetadata("instance-id")
	}

	helper := mp.NewMackerelPlugin(ec2)
	helper.Tempfile = *optTempfile

	if os.Getenv("MACKEREL_AGENT_PLUGIN_META") != "" {
		helper.OutputDefinitions()
	} else {
		helper.OutputValues()
	}
}
<commit_msg>Revert "[aws-ec2] don't set default tempfile name by plugin"<commit_after>package mpawsec2

import (
	"errors"
	"flag"
	"fmt"
	"log"
	"os"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/ec2metadata"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/cloudwatch"

	mp "github.com/mackerelio/go-mackerel-plugin"
)

// EC2Plugin is a mackerel plugin for ec2
type EC2Plugin struct {
	InstanceID  string
	Region      string
	Credentials *credentials.Credentials
	CloudWatch  *cloudwatch.CloudWatch
}

var graphdef = map[string]mp.Graphs{
	"ec2.CPUUtilization": {
		Label: "CPU Utilization",
		Unit:  "percentage",
		Metrics: []mp.Metrics{
			{Name: "CPUUtilization", Label: "CPUUtilization"},
		},
	},
	"ec2.DiskBytes": {
		Label: "Disk Bytes",
		Unit:  "bytes",
		Metrics: []mp.Metrics{
			{Name: "DiskReadBytes", Label: "DiskReadBytes"},
			{Name: "DiskWriteBytes", Label: "DiskWriteBytes"},
		},
	},
	"ec2.DiskOps": {
		Label: "Disk Ops",
		Unit:  "float",
		Metrics: []mp.Metrics{
			{Name: "DiskReadOps", Label: "DiskReadOps"},
			{Name: "DiskWriteOps", Label: "DiskWriteOps"},
		},
	},
	"ec2.Network": {
		Label: "Network",
		Unit:  "bytes",
		Metrics: []mp.Metrics{
			{Name: "NetworkIn", Label: "NetworkIn"},
			{Name: "NetworkOut", Label: "NetworkOut"},
		},
	},
	"ec2.NetworkPackets": {
		Label: "Network",
		Unit:  "bytes",
		Metrics: []mp.Metrics{
			{Name: "NetworkPacketsIn", Label: "NetworkPacketsIn"},
			{Name: "NetworkPacketsOut", Label: "NetworkPacketsOut"},
		},
	},
	"ec2.StatusCheckFailed": {
		Label: "StatusCheckFailed",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: "StatusCheckFailed", Label: "StatusCheckFailed"},
			{Name: "StatusCheckFailed_Instance", Label: "StatusCheckFailed_Instance"},
			{Name: "StatusCheckFailed_System", Label: "StatusCheckFailed_System"},
		},
	},
}

// GraphDefinition returns graphdef
func (p EC2Plugin) GraphDefinition() map[string]mp.Graphs {
	return graphdef
}

func getLastPoint(cloudWatch *cloudwatch.CloudWatch, dimension *cloudwatch.Dimension, metricName string) (float64, error) {
	now := time.Now()
	response, err := cloudWatch.GetMetricStatistics(&cloudwatch.GetMetricStatisticsInput{
		StartTime:  aws.Time(now.Add(time.Duration(600) * time.Second * -1)),
		EndTime:    aws.Time(now),
		Period:     aws.Int64(60),
		Namespace:  aws.String("AWS/EC2"),
		Dimensions: []*cloudwatch.Dimension{dimension},
		MetricName: aws.String(metricName),
		Statistics: []*string{aws.String("Average")},
	})
	if err != nil {
		return 0, err
	}

	datapoints := response.Datapoints
	if len(datapoints) == 0 {
		return 0, errors.New("fetched no datapoints")
	}

	latest := time.Unix(0, 0)
	var latestVal float64
	for _, dp := range datapoints {
		if dp.Timestamp.Before(latest) {
			continue
		}

		latest = *dp.Timestamp
		latestVal = *dp.Average
	}

	return latestVal, nil
}

// FetchMetrics fetches metrics from CloudWatch
func (p EC2Plugin) FetchMetrics() (map[string]float64, error) {
	stat := make(map[string]float64)
	p.CloudWatch = cloudwatch.New(session.New(
		&aws.Config{
			Credentials: p.Credentials,
			Region:      &p.Region,
		}))
	dimension := &cloudwatch.Dimension{
		Name:  aws.String("InstanceId"),
		Value: aws.String(p.InstanceID),
	}

	for _, met := range [...]string{
		"CPUUtilization",
		"DiskReadBytes",
		"DiskReadOps",
		"DiskWriteBytes",
		"DiskWriteOps",
		"NetworkIn",
		"NetworkOut",
		"NetworkPacketsIn",
		"NetworkPacketsOut",
		"StatusCheckFailed",
		"StatusCheckFailed_Instance",
		"StatusCheckFailed_System",
	} {
		v, err := getLastPoint(p.CloudWatch, dimension, met)
		if err == nil {
			stat[met] = v
		} else {
			log.Printf("%s: %s", met, err)
		}
	}

	return stat, nil
}

// Do the plugin
func Do() {
	optAccessKeyID := flag.String("access-key-id", "", "AWS Access Key ID")
	optSecretAccessKey := flag.String("secret-access-key", "", "AWS Secret Access Key")
	optRegion := flag.String("region", "", "AWS Region")
	optInstanceID := flag.String("instance-id", "", "Instance ID")
	optTempfile := flag.String("tempfile", "", "Temp file name")
	flag.Parse()

	var ec2 EC2Plugin

	// use credentials from option
	if *optAccessKeyID != "" && *optSecretAccessKey != "" {
		ec2.Credentials = credentials.NewStaticCredentials(*optAccessKeyID, *optSecretAccessKey, "")
	}

	// get metadata in ec2 instance
	ec2MC := ec2metadata.New(session.New())
	ec2.Region = *optRegion
	if *optRegion == "" {
		ec2.Region, _ = ec2MC.Region()
	}
	ec2.InstanceID = *optInstanceID
	if *optInstanceID == "" {
		ec2.InstanceID, _ = ec2MC.GetMetadata("instance-id")
	}

	helper := mp.NewMackerelPlugin(ec2)
	if *optTempfile != "" {
		helper.Tempfile = *optTempfile
	} else {
		helper.Tempfile = fmt.Sprintf("/tmp/mackerel-plugin-aws-ec2-%s", ec2.InstanceID)
	}

	if os.Getenv("MACKEREL_AGENT_PLUGIN_META") != "" {
		helper.OutputDefinitions()
	} else {
		helper.OutputValues()
	}
}
<|endoftext|>


<commit_before>package mphaproxy

import (
	"bufio"
	"encoding/csv"
	"errors"
	"flag"
	"fmt"
	"io"
	"net"
	"net/http"
	"strconv"
	"time"

	mp "github.com/mackerelio/go-mackerel-plugin"
)

var graphdef = map[string]mp.Graphs{
	"haproxy.total.sessions": {
		Label: "HAProxy Total Sessions",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: "sessions", Label: "Sessions", Diff: true},
		},
	},
	"haproxy.total.bytes": {
		Label: "HAProxy Total Bytes",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: "bytes_in", Label: "Bytes In", Diff: true},
			{Name: "bytes_out", Label: "Bytes Out", Diff: true},
		},
	},
	"haproxy.total.connection_errors": {
		Label: "HAProxy Total Connection Errors",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: "connection_errors", Label: "Connection Errors", Diff: true},
		},
	},
}

// HAProxyPlugin mackerel plugin for haproxy
type HAProxyPlugin struct {
	URI      string
	Username string
	Password string
	Socket   string
}

// FetchMetrics interface for mackerelplugin
func (p HAProxyPlugin) FetchMetrics() (map[string]float64, error) {
	var metrics map[string]float64
	var err error
	if p.Socket == "" {
		metrics, err = p.fetchMetricsFromTCP()
	} else {
		metrics, err = p.fetchMetricsFromSocket()
	}
	return metrics, err
}

func (p HAProxyPlugin) fetchMetricsFromTCP() (map[string]float64, error) {
	client := &http.Client{
		Timeout: time.Duration(5) * time.Second,
	}

	requestURI := p.URI + ";csv;norefresh"
	req, err := http.NewRequest("GET", requestURI, nil)
	if err != nil {
		return nil, err
	}
	if p.Username != "" {
		req.SetBasicAuth(p.Username, p.Password)
	}

	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	if resp.StatusCode != 200 {
		return nil, fmt.Errorf("Request failed. Status: %s, URI: %s", resp.Status, requestURI)
	}

	return p.parseStats(resp.Body)
}

func (p HAProxyPlugin) fetchMetricsFromSocket() (map[string]float64, error) {
	client, err := net.Dial("unix", p.Socket)
	if err != nil {
		return nil, err
	}
	defer client.Close()

	fmt.Fprintln(client, "show stat")

	return p.parseStats(bufio.NewReader(client))
}

func (p HAProxyPlugin) parseStats(statsBody io.Reader) (map[string]float64, error) {
	stat := make(map[string]float64)
	reader := csv.NewReader(statsBody)

	for {
		columns, err := reader.Read()
		if err == io.EOF {
			break
		}

		if len(columns) < 60 {
			return nil, errors.New("length of stats csv is too short (specified uri/socket may be wrong)")
		}

		if columns[1] != "BACKEND" {
			continue
		}

		var data float64

		data, err = strconv.ParseFloat(columns[7], 64)
		if err != nil {
			return nil, errors.New("cannot get values")
		}
		stat["sessions"] += data

		data, err = strconv.ParseFloat(columns[8], 64)
		if err != nil {
			return nil, errors.New("cannot get values")
		}
		stat["bytes_in"] += data

		data, err = strconv.ParseFloat(columns[9], 64)
		if err != nil {
			return nil, errors.New("cannot get values")
		}
		stat["bytes_out"] += data

		data, err = strconv.ParseFloat(columns[13], 64)
		if err != nil {
			return nil, errors.New("cannot get values")
		}
		stat["connection_errors"] += data
	}

	return stat, nil
}

// GraphDefinition interface for mackerelplugin
func (p HAProxyPlugin) GraphDefinition() map[string]mp.Graphs {
	return graphdef
}

// Do the plugin
func Do() {
	optURI := flag.String("uri", "", "URI")
	optScheme := flag.String("scheme", "http", "Scheme")
	optHost := flag.String("host", "localhost", "Hostname")
	optPort := flag.String("port", "80", "Port")
	optPath := flag.String("path", "/", "Path")
	optUsername := flag.String("username", "", "Username for Basic Auth")
	optPassword := flag.String("password", "", "Password for Basic Auth")
	optTempfile := flag.String("tempfile", "", "Temp file name")
	optSocket := flag.String("socket", "", "Unix Domain Socket")
	flag.Parse()

	var haproxy HAProxyPlugin
	if *optURI != "" {
		haproxy.URI = *optURI
	} else {
		haproxy.URI = fmt.Sprintf("%s://%s:%s%s", *optScheme, *optHost, *optPort, *optPath)
	}

	if *optUsername != "" {
		haproxy.Username = *optUsername
	}

	if *optPassword != "" {
		haproxy.Password = *optPassword
	}

	if *optSocket != "" {
		haproxy.Socket = *optSocket
	}

	helper := mp.NewMackerelPlugin(haproxy)
	helper.Tempfile = *optTempfile

	helper.Run()
}
<commit_msg>update mackerel-plugin-haproxy for setting password via environment variable.<commit_after>package mphaproxy

import (
	"bufio"
	"encoding/csv"
	"errors"
	"flag"
	"fmt"
	"io"
	"net"
	"net/http"
	"os"
	"strconv"
	"time"

	mp "github.com/mackerelio/go-mackerel-plugin"
)

var graphdef = map[string]mp.Graphs{
	"haproxy.total.sessions": {
		Label: "HAProxy Total Sessions",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: "sessions", Label: "Sessions", Diff: true},
		},
	},
	"haproxy.total.bytes": {
		Label: "HAProxy Total Bytes",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: "bytes_in", Label: "Bytes In", Diff: true},
			{Name: "bytes_out", Label: "Bytes Out", Diff: true},
		},
	},
	"haproxy.total.connection_errors": {
		Label: "HAProxy Total Connection Errors",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: "connection_errors", Label: "Connection Errors", Diff: true},
		},
	},
}

// HAProxyPlugin mackerel plugin for haproxy
type HAProxyPlugin struct {
	URI      string
	Username string
	Password string
	Socket   string
}

// FetchMetrics interface for mackerelplugin
func (p HAProxyPlugin) FetchMetrics() (map[string]float64, error) {
	var metrics map[string]float64
	var err error
	if p.Socket == "" {
		metrics, err = p.fetchMetricsFromTCP()
	} else {
		metrics, err = p.fetchMetricsFromSocket()
	}
	return metrics, err
}

func (p HAProxyPlugin) fetchMetricsFromTCP() (map[string]float64, error) {
	client := &http.Client{
		Timeout: time.Duration(5) * time.Second,
	}

	requestURI := p.URI + ";csv;norefresh"
	req, err := http.NewRequest("GET", requestURI, nil)
	if err != nil {
		return nil, err
	}
	if p.Username != "" {
		req.SetBasicAuth(p.Username, p.Password)
	}

	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	if resp.StatusCode != 200 {
		return nil, fmt.Errorf("Request failed. Status: %s, URI: %s", resp.Status, requestURI)
	}

	return p.parseStats(resp.Body)
}

func (p HAProxyPlugin) fetchMetricsFromSocket() (map[string]float64, error) {
	client, err := net.Dial("unix", p.Socket)
	if err != nil {
		return nil, err
	}
	defer client.Close()

	fmt.Fprintln(client, "show stat")

	return p.parseStats(bufio.NewReader(client))
}

func (p HAProxyPlugin) parseStats(statsBody io.Reader) (map[string]float64, error) {
	stat := make(map[string]float64)
	reader := csv.NewReader(statsBody)

	for {
		columns, err := reader.Read()
		if err == io.EOF {
			break
		}

		if len(columns) < 60 {
			return nil, errors.New("length of stats csv is too short (specified uri/socket may be wrong)")
		}

		if columns[1] != "BACKEND" {
			continue
		}

		var data float64

		data, err = strconv.ParseFloat(columns[7], 64)
		if err != nil {
			return nil, errors.New("cannot get values")
		}
		stat["sessions"] += data

		data, err = strconv.ParseFloat(columns[8], 64)
		if err != nil {
			return nil, errors.New("cannot get values")
		}
		stat["bytes_in"] += data

		data, err = strconv.ParseFloat(columns[9], 64)
		if err != nil {
			return nil, errors.New("cannot get values")
		}
		stat["bytes_out"] += data

		data, err = strconv.ParseFloat(columns[13], 64)
		if err != nil {
			return nil, errors.New("cannot get values")
		}
		stat["connection_errors"] += data
	}

	return stat, nil
}

// GraphDefinition interface for mackerelplugin
func (p HAProxyPlugin) GraphDefinition() map[string]mp.Graphs {
	return graphdef
}

// Do the plugin
func Do() {
	optURI := flag.String("uri", "", "URI")
	optScheme := flag.String("scheme", "http", "Scheme")
	optHost := flag.String("host", "localhost", "Hostname")
	optPort := flag.String("port", "80", "Port")
	optPath := flag.String("path", "/", "Path")
	optUsername := flag.String("username", "", "Username for Basic Auth")
	optPassword := flag.String("password", os.Getenv("HAPROXY_PASSWORD"), "Password for Basic Auth")
	optTempfile := flag.String("tempfile", "", "Temp file name")
	optSocket := flag.String("socket", "", "Unix Domain Socket")
	flag.Parse()

	var haproxy HAProxyPlugin
	if *optURI != "" {
		haproxy.URI = *optURI
	} else {
		haproxy.URI = fmt.Sprintf("%s://%s:%s%s", *optScheme, *optHost, *optPort, *optPath)
	}

	if *optUsername != "" {
		haproxy.Username = *optUsername
	}

	if *optPassword != "" {
		haproxy.Password = *optPassword
	}

	if *optSocket != "" {
		haproxy.Socket = *optSocket
	}

	helper := mp.NewMackerelPlugin(haproxy)
	helper.Tempfile = *optTempfile

	helper.Run()
}
<|endoftext|>