<commit_before>// NOTE: SHOULD BE RUN FROM run_tests directory
// note: deploy2deter must be run from within it's directory
//
// Outputting data: output to csv files (for loading into excel)
//   make a datastructure per test output file
//   all output should be in the test_data subdirectory
//
// connect with logging server (receive json until "EOF" seen or "terminating")
//   connect to websocket ws://localhost:8080/log
//   receive each message as bytes
//		 if bytes contains "EOF" or contains "terminating"
//       wrap up the round, output to test_data directory, kill deploy2deter
//
// for memstats check localhost:8080/d/server-0-0/debug/vars
//   parse out the memstats zones that we are concerned with
//
// different graphs needed rounds:
//   load on the x-axis: increase messages per round holding everything else constant
//			hpn=40 bf=10, bf=50
//
// run time command with the deploy2deter exec.go (timestamper) instance associated with the root
//    and a random set of servers
//
// latency on y-axis, timestamp servers on x-axis push timestampers as higher as possible
//
//
// RunTest(hpn, bf), Monitor() -> RunStats() -> csv -> Excel
//
package main

import (
	"errors"
	"fmt"
	"log"
	"math"
	"os"
	"os/exec"
	"strconv"
	"time"
)

type T struct {
	nmachs      int
	hpn         int
	bf          int
	rate        int
	rounds      int
	failures    int
	rFail       int
	fFail       int
	testConnect bool
}

var DefaultMachs int = 32

// time-per-round * DefaultRounds = 10 * 20 = 3.3 minutes now
// this leaves us with 7 minutes for test setup and tear-down
var DefaultRounds int = 20

var view bool
var debug string = "-debug=false"

// hpn, bf, nmsgsG
func RunTest(t T) (RunStats, error) {
	// add timeout for 10 minutes?
	done := make(chan struct{})
	var rs RunStats
	nmachs := fmt.Sprintf("-nmachs=%d", t.nmachs)
	hpn := fmt.Sprintf("-hpn=%d", t.hpn)
	nmsgs := fmt.Sprintf("-nmsgs=%d", -1)
	bf := fmt.Sprintf("-bf=%d", t.bf)
	rate := fmt.Sprintf("-rate=%d", t.rate)
	rounds := fmt.Sprintf("-rounds=%d", t.rounds)
	failures := fmt.Sprintf("-failures=%d", t.failures)
	rFail := fmt.Sprintf("-rfail=%d", t.rFail)
	fFail := fmt.Sprintf("-ffail=%d", t.fFail)
	tcon := fmt.Sprintf("-test_connect=%t", t.testConnect)
	cmd := exec.Command("./deploy2deter", nmachs, hpn, nmsgs, bf, rate, rounds, debug, failures, rFail, fFail, tcon)
	log.Println("RUNNING TEST:", cmd.Args)
	log.Println("FAILURES PERCENT:", t.failures)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	err := cmd.Start()
	if err != nil {
		log.Fatal(err)
	}

	// give it a while to start up
	time.Sleep(30 * time.Second)

	go func() {
		rs = Monitor(t.bf)
		cmd.Process.Kill()
		fmt.Println("TEST COMPLETE:", rs)
		done <- struct{}{}
	}()

	// timeout the command if it takes too long
	select {
	case <-done:
		if isZero(rs.MinTime) || isZero(rs.MaxTime) || isZero(rs.AvgTime) || math.IsNaN(rs.Rate) || math.IsInf(rs.Rate, 0) {
			return rs, errors.New("unable to get good data")
		}
		return rs, nil
	case <-time.After(10 * time.Minute):
		return rs, errors.New("timed out")
	}
}

// RunTests runs the given tests and puts the output into the
// given file name. It outputs RunStats in a CSV format.
func RunTests(name string, ts []T) {

	rs := make([]RunStats, len(ts))
	f, err := os.OpenFile(TestFile(name), os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0660)
	if err != nil {
		log.Fatal("error opening test file:", err)
	}
	_, err = f.Write(rs[0].CSVHeader())
	if err != nil {
		log.Fatal("error writing test file header:", err)
	}
	err = f.Sync()
	if err != nil {
		log.Fatal("error syncing test file:", err)
	}

	nTimes := 2
	stopOnSuccess := true
	for i, t := range ts {
		// run test t nTimes times
		// take the average of all successfull runs
		var runs []RunStats
		for r := 0; r < nTimes; r++ {
			run, err := RunTest(t)
			if err != nil {
				log.Println("error running test:", err)
			}

			// Clean Up after test
			log.Println("KILLING REMAINING PROCESSES")
			cmd := exec.Command("./deploy2deter", "-kill=true")
			cmd.Stdout = os.Stdout
			cmd.Stderr = os.Stderr
			cmd.Run()
			if err == nil {
				runs = append(runs, run)
				if stopOnSuccess {
					break
				}
			}

		}

		if len(runs) == 0 {
			log.Println("unable to get any data for test:", t)
			continue
		}

		rs[i] = RunStatsAvg(runs)
		_, err := f.Write(rs[i].CSV())
		if err != nil {
			log.Fatal("error writing data to test file:", err)
		}
		err = f.Sync()
		if err != nil {
			log.Fatal("error syncing data to test file:", err)
		}

		cl, err := os.OpenFile(
			TestFile("client_latency_"+name+"_"+strconv.Itoa(i)),
			os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0660)
		if err != nil {
			log.Fatal("error opening test file:", err)
		}
		_, err = cl.Write(rs[i].TimesCSV())
		if err != nil {
			log.Fatal("error writing client latencies to file:", err)
		}
		err = cl.Sync()
		if err != nil {
			log.Fatal("error syncing data to latency file:", err)
		}
		cl.Close()

	}
}

// high and low specify how many milliseconds between messages
func RateLoadTest(hpn, bf int) []T {
	return []T{
		{DefaultMachs, hpn, bf, 5000, DefaultRounds, 0, 0, 0, false}, // never send a message
		{DefaultMachs, hpn, bf, 5000, DefaultRounds, 0, 0, 0, false}, // one per round
		{DefaultMachs, hpn, bf, 500, DefaultRounds, 0, 0, 0, false},  // 10 per round
		{DefaultMachs, hpn, bf, 50, DefaultRounds, 0, 0, 0, false},   // 100 per round
		{DefaultMachs, hpn, bf, 30, DefaultRounds, 0, 0, 0, false},   // 1000 per round
	}
}

func DepthTest(hpn, low, high, step int) []T {
	ts := make([]T, 0)
	for bf := low; bf <= high; bf += step {
		ts = append(ts, T{DefaultMachs, hpn, bf, 10, DefaultRounds, 0, 0, 0, false})
	}
	return ts
}

func DepthTestFixed(hpn int) []T {
	return []T{
		{DefaultMachs, hpn, 1, 30, DefaultRounds, 0, 0, 0, false},
		{DefaultMachs, hpn, 2, 30, DefaultRounds, 0, 0, 0, false},
		{DefaultMachs, hpn, 4, 30, DefaultRounds, 0, 0, 0, false},
		{DefaultMachs, hpn, 8, 30, DefaultRounds, 0, 0, 0, false},
		{DefaultMachs, hpn, 16, 30, DefaultRounds, 0, 0, 0, false},
		{DefaultMachs, hpn, 32, 30, DefaultRounds, 0, 0, 0, false},
		{DefaultMachs, hpn, 64, 30, DefaultRounds, 0, 0, 0, false},
		{DefaultMachs, hpn, 128, 30, DefaultRounds, 0, 0, 0, false},
		{DefaultMachs, hpn, 256, 30, DefaultRounds, 0, 0, 0, false},
		{DefaultMachs, hpn, 512, 30, DefaultRounds, 0, 0, 0, false},
	}
}

func ScaleTest(bf, low, high, mult int) []T {
	ts := make([]T, 0)
	for hpn := low; hpn <= high; hpn *= mult {
		ts = append(ts, T{DefaultMachs, hpn, bf, 10, DefaultRounds, 0, 0, 0, false})
	}
	return ts
}

// nmachs=32, hpn=128, bf=16, rate=500, failures=20, root failures, failures
var FailureTests = []T{
	{DefaultMachs, 64, 16, 30, 50, 0, 0, 0, false},
	{DefaultMachs, 64, 16, 30, 50, 0, 5, 0, false},
	{DefaultMachs, 64, 16, 30, 50, 0, 10, 0, false},
	{DefaultMachs, 64, 16, 30, 50, 5, 0, 5, false},
	{DefaultMachs, 64, 16, 30, 50, 5, 0, 10, false},
	{DefaultMachs, 64, 16, 30, 50, 5, 0, 10, true},
}

var VotingTest = []T{
	{DefaultMachs, 64, 16, 30, 50, 0, 0, 0, true},
	{DefaultMachs, 64, 16, 30, 50, 0, 0, 0, false},
}

func FullTests() []T {
	var nmachs = []int{1, 16, 32}
	var hpns = []int{1, 16, 32, 128}
	var bfs = []int{2, 4, 8, 16, 128}
	var rates = []int{5000, 500, 100, 30}
	failures := 0

	var tests []T
	for _, nmach := range nmachs {
		for _, hpn := range hpns {
			for _, bf := range bfs {
				for _, rate := range rates {
					tests = append(tests, T{nmach, hpn, bf, rate, DefaultRounds, failures, 0, 0, false})
				}
			}
		}
	}

	return tests
}

var HostsTest = []T{
	{DefaultMachs, 1, 2, 30, 20, 0, 0, 0, false},
	{DefaultMachs, 2, 3, 30, 20, 0, 0, 0, false},
	{DefaultMachs, 4, 3, 30, 20, 0, 0, 0, false},
	{DefaultMachs, 8, 8, 30, 20, 0, 0, 0, false},
	{DefaultMachs, 16, 16, 30, 20, 0, 0, 0, false},
	{DefaultMachs, 32, 16, 30, 20, 0, 0, 0, false},
	{DefaultMachs, 64, 16, 30, 20, 0, 0, 0, false},
	{DefaultMachs, 128, 16, 30, 50, 0, 0, 0, false},
}

func main() {
	SetDebug(false)
	// view = true
	os.Chdir("..")

	MkTestDir()

	err := exec.Command("go", "build", "-v").Run()
	if err != nil {
		log.Fatalln("error building deploy2deter:", err)
	}
	log.Println("KILLING REMAINING PROCESSES")
	cmd := exec.Command("./deploy2deter", "-kill=true")
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Run()

	// test the testing framework

	// DefaultRounds = 5
	// t := FailureTests
	// RunTests("failure_test.csv", t)
	RunTests("vote_test", VotingTest)
	RunTests("failure_test", FailureTests)
	// RunTests("host_test", HostsTest)
	// t := FailureTests
	// RunTests("failure_test", t)
	// t = ScaleTest(10, 1, 100, 2)
	// RunTests("scale_test.csv", t)
	// how does the branching factor effect speed
	// t = DepthTestFixed(100)
	// RunTests("depth_test.csv", t)

	// load test the client
	// t = RateLoadTest(40, 10)
	// RunTests("load_rate_test_bf10.csv", t)
	// t = RateLoadTest(40, 50)
	// RunTests("load_rate_test_bf50.csv", t)

}

func MkTestDir() {
	err := os.MkdirAll("test_data/", 0777)
	if err != nil {
		log.Fatal("failed to make test directory")
	}
}

func TestFile(name string) string {
	return "test_data/" + name + ".csv"
}

func SetDebug(b bool) {
	if b {
		debug = "-debug=true"
	} else {
		debug = "-debug=false"
	}
}

func isZero(f float64) bool {
	return math.Abs(f) < 0.0000001
}
<commit_msg>made test for voting<commit_after>// NOTE: SHOULD BE RUN FROM run_tests directory
// note: deploy2deter must be run from within it's directory
//
// Outputting data: output to csv files (for loading into excel)
//   make a datastructure per test output file
//   all output should be in the test_data subdirectory
//
// connect with logging server (receive json until "EOF" seen or "terminating")
//   connect to websocket ws://localhost:8080/log
//   receive each message as bytes
//		 if bytes contains "EOF" or contains "terminating"
//       wrap up the round, output to test_data directory, kill deploy2deter
//
// for memstats check localhost:8080/d/server-0-0/debug/vars
//   parse out the memstats zones that we are concerned with
//
// different graphs needed rounds:
//   load on the x-axis: increase messages per round holding everything else constant
//			hpn=40 bf=10, bf=50
//
// run time command with the deploy2deter exec.go (timestamper) instance associated with the root
//    and a random set of servers
//
// latency on y-axis, timestamp servers on x-axis push timestampers as higher as possible
//
//
// RunTest(hpn, bf), Monitor() -> RunStats() -> csv -> Excel
//
package main

import (
	"errors"
	"fmt"
	"log"
	"math"
	"os"
	"os/exec"
	"strconv"
	"time"
)

type T struct {
	nmachs      int
	hpn         int
	bf          int
	rate        int
	rounds      int
	failures    int
	rFail       int
	fFail       int
	testConnect bool
}

var DefaultMachs int = 32

// time-per-round * DefaultRounds = 10 * 20 = 3.3 minutes now
// this leaves us with 7 minutes for test setup and tear-down
var DefaultRounds int = 20

var view bool
var debug string = "-debug=false"

// hpn, bf, nmsgsG
func RunTest(t T) (RunStats, error) {
	// add timeout for 10 minutes?
	done := make(chan struct{})
	var rs RunStats
	nmachs := fmt.Sprintf("-nmachs=%d", t.nmachs)
	hpn := fmt.Sprintf("-hpn=%d", t.hpn)
	nmsgs := fmt.Sprintf("-nmsgs=%d", -1)
	bf := fmt.Sprintf("-bf=%d", t.bf)
	rate := fmt.Sprintf("-rate=%d", t.rate)
	rounds := fmt.Sprintf("-rounds=%d", t.rounds)
	failures := fmt.Sprintf("-failures=%d", t.failures)
	rFail := fmt.Sprintf("-rfail=%d", t.rFail)
	fFail := fmt.Sprintf("-ffail=%d", t.fFail)
	tcon := fmt.Sprintf("-test_connect=%t", t.testConnect)
	cmd := exec.Command("./deploy2deter", nmachs, hpn, nmsgs, bf, rate, rounds, debug, failures, rFail, fFail, tcon)
	log.Println("RUNNING TEST:", cmd.Args)
	log.Println("FAILURES PERCENT:", t.failures)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	err := cmd.Start()
	if err != nil {
		log.Fatal(err)
	}

	// give it a while to start up
	time.Sleep(30 * time.Second)

	go func() {
		rs = Monitor(t.bf)
		cmd.Process.Kill()
		fmt.Println("TEST COMPLETE:", rs)
		done <- struct{}{}
	}()

	// timeout the command if it takes too long
	select {
	case <-done:
		if isZero(rs.MinTime) || isZero(rs.MaxTime) || isZero(rs.AvgTime) || math.IsNaN(rs.Rate) || math.IsInf(rs.Rate, 0) {
			return rs, errors.New("unable to get good data")
		}
		return rs, nil
	case <-time.After(10 * time.Minute):
		return rs, errors.New("timed out")
	}
}

// RunTests runs the given tests and puts the output into the
// given file name. It outputs RunStats in a CSV format.
func RunTests(name string, ts []T) {

	rs := make([]RunStats, len(ts))
	f, err := os.OpenFile(TestFile(name), os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0660)
	if err != nil {
		log.Fatal("error opening test file:", err)
	}
	_, err = f.Write(rs[0].CSVHeader())
	if err != nil {
		log.Fatal("error writing test file header:", err)
	}
	err = f.Sync()
	if err != nil {
		log.Fatal("error syncing test file:", err)
	}

	nTimes := 2
	stopOnSuccess := true
	for i, t := range ts {
		// run test t nTimes times
		// take the average of all successfull runs
		var runs []RunStats
		for r := 0; r < nTimes; r++ {
			run, err := RunTest(t)
			if err != nil {
				log.Println("error running test:", err)
			}

			// Clean Up after test
			log.Println("KILLING REMAINING PROCESSES")
			cmd := exec.Command("./deploy2deter", "-kill=true")
			cmd.Stdout = os.Stdout
			cmd.Stderr = os.Stderr
			cmd.Run()
			if err == nil {
				runs = append(runs, run)
				if stopOnSuccess {
					break
				}
			}

		}

		if len(runs) == 0 {
			log.Println("unable to get any data for test:", t)
			continue
		}

		rs[i] = RunStatsAvg(runs)
		_, err := f.Write(rs[i].CSV())
		if err != nil {
			log.Fatal("error writing data to test file:", err)
		}
		err = f.Sync()
		if err != nil {
			log.Fatal("error syncing data to test file:", err)
		}

		cl, err := os.OpenFile(
			TestFile("client_latency_"+name+"_"+strconv.Itoa(i)),
			os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0660)
		if err != nil {
			log.Fatal("error opening test file:", err)
		}
		_, err = cl.Write(rs[i].TimesCSV())
		if err != nil {
			log.Fatal("error writing client latencies to file:", err)
		}
		err = cl.Sync()
		if err != nil {
			log.Fatal("error syncing data to latency file:", err)
		}
		cl.Close()

	}
}

// high and low specify how many milliseconds between messages
func RateLoadTest(hpn, bf int) []T {
	return []T{
		{DefaultMachs, hpn, bf, 5000, DefaultRounds, 0, 0, 0, false}, // never send a message
		{DefaultMachs, hpn, bf, 5000, DefaultRounds, 0, 0, 0, false}, // one per round
		{DefaultMachs, hpn, bf, 500, DefaultRounds, 0, 0, 0, false},  // 10 per round
		{DefaultMachs, hpn, bf, 50, DefaultRounds, 0, 0, 0, false},   // 100 per round
		{DefaultMachs, hpn, bf, 30, DefaultRounds, 0, 0, 0, false},   // 1000 per round
	}
}

func DepthTest(hpn, low, high, step int) []T {
	ts := make([]T, 0)
	for bf := low; bf <= high; bf += step {
		ts = append(ts, T{DefaultMachs, hpn, bf, 10, DefaultRounds, 0, 0, 0, false})
	}
	return ts
}

func DepthTestFixed(hpn int) []T {
	return []T{
		{DefaultMachs, hpn, 1, 30, DefaultRounds, 0, 0, 0, false},
		{DefaultMachs, hpn, 2, 30, DefaultRounds, 0, 0, 0, false},
		{DefaultMachs, hpn, 4, 30, DefaultRounds, 0, 0, 0, false},
		{DefaultMachs, hpn, 8, 30, DefaultRounds, 0, 0, 0, false},
		{DefaultMachs, hpn, 16, 30, DefaultRounds, 0, 0, 0, false},
		{DefaultMachs, hpn, 32, 30, DefaultRounds, 0, 0, 0, false},
		{DefaultMachs, hpn, 64, 30, DefaultRounds, 0, 0, 0, false},
		{DefaultMachs, hpn, 128, 30, DefaultRounds, 0, 0, 0, false},
		{DefaultMachs, hpn, 256, 30, DefaultRounds, 0, 0, 0, false},
		{DefaultMachs, hpn, 512, 30, DefaultRounds, 0, 0, 0, false},
	}
}

func ScaleTest(bf, low, high, mult int) []T {
	ts := make([]T, 0)
	for hpn := low; hpn <= high; hpn *= mult {
		ts = append(ts, T{DefaultMachs, hpn, bf, 10, DefaultRounds, 0, 0, 0, false})
	}
	return ts
}

// nmachs=32, hpn=128, bf=16, rate=500, failures=20, root failures, failures
var FailureTests = []T{
	{DefaultMachs, 64, 16, 30, 50, 0, 0, 0, false},
	{DefaultMachs, 64, 16, 30, 50, 0, 5, 0, false},
	{DefaultMachs, 64, 16, 30, 50, 0, 10, 0, false},
	{DefaultMachs, 64, 16, 30, 50, 5, 0, 5, false},
	{DefaultMachs, 64, 16, 30, 50, 5, 0, 10, false},
	{DefaultMachs, 64, 16, 30, 50, 5, 0, 10, true},
}

var VotingTest = []T{
	{DefaultMachs, 64, 16, 30, 50, 0, 0, 0, true},
	{DefaultMachs, 64, 16, 30, 50, 0, 0, 0, false},
}

func FullTests() []T {
	var nmachs = []int{1, 16, 32}
	var hpns = []int{1, 16, 32, 128}
	var bfs = []int{2, 4, 8, 16, 128}
	var rates = []int{5000, 500, 100, 30}
	failures := 0

	var tests []T
	for _, nmach := range nmachs {
		for _, hpn := range hpns {
			for _, bf := range bfs {
				for _, rate := range rates {
					tests = append(tests, T{nmach, hpn, bf, rate, DefaultRounds, failures, 0, 0, false})
				}
			}
		}
	}

	return tests
}

var HostsTest = []T{
	{DefaultMachs, 1, 2, 30, 20, 0, 0, 0, false},
	{DefaultMachs, 2, 3, 30, 20, 0, 0, 0, false},
	{DefaultMachs, 4, 3, 30, 20, 0, 0, 0, false},
	{DefaultMachs, 8, 8, 30, 20, 0, 0, 0, false},
	{DefaultMachs, 16, 16, 30, 20, 0, 0, 0, false},
	{DefaultMachs, 32, 16, 30, 20, 0, 0, 0, false},
	{DefaultMachs, 64, 16, 30, 20, 0, 0, 0, false},
	{DefaultMachs, 128, 16, 30, 50, 0, 0, 0, false},
}
var VTest = []T{
	{DefaultMachs, 1, 2, 10000000, 50, 0, 0, 0, false},
}

func main() {
	SetDebug(false)
	// view = true
	os.Chdir("..")

	MkTestDir()

	err := exec.Command("go", "build", "-v").Run()
	if err != nil {
		log.Fatalln("error building deploy2deter:", err)
	}
	log.Println("KILLING REMAINING PROCESSES")
	cmd := exec.Command("./deploy2deter", "-kill=true")
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Run()

	// test the testing framework
	RunTests("vote_test_no_signing.csv", VTest)
	// DefaultRounds = 5
	// t := FailureTests
	// RunTests("failure_test.csv", t)
	// RunTests("vote_test", VotingTest)
	// RunTests("failure_test", FailureTests)
	// RunTests("host_test", HostsTest)
	// t := FailureTests
	// RunTests("failure_test", t)
	// t = ScaleTest(10, 1, 100, 2)
	// RunTests("scale_test.csv", t)
	// how does the branching factor effect speed
	// t = DepthTestFixed(100)
	// RunTests("depth_test.csv", t)

	// load test the client
	// t = RateLoadTest(40, 10)
	// RunTests("load_rate_test_bf10.csv", t)
	// t = RateLoadTest(40, 50)
	// RunTests("load_rate_test_bf50.csv", t)

}

func MkTestDir() {
	err := os.MkdirAll("test_data/", 0777)
	if err != nil {
		log.Fatal("failed to make test directory")
	}
}

func TestFile(name string) string {
	return "test_data/" + name + ".csv"
}

func SetDebug(b bool) {
	if b {
		debug = "-debug=true"
	} else {
		debug = "-debug=false"
	}
}

func isZero(f float64) bool {
	return math.Abs(f) < 0.0000001
}
<|endoftext|>


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

package oglematchers

import (
	"errors"
	"fmt"
	"math"
	"reflect"
)

// LessThan returns a matcher that matches integer, floating point, or strings
// values v such that v < x. Comparison is not defined between numeric and
// string types, but is defined between all integer and floating point types.
//
// x must itself be an integer, floating point, or string type; otherwise,
// LessThan will panic.
func LessThan(x interface{}) Matcher {
	v := reflect.ValueOf(x)
	kind := v.Kind()

	switch {
	case isInteger(v):
	case isFloat(v):
	case kind == reflect.String:

	default:
		panic(fmt.Sprintf("LessThan: unexpected kind %v", kind))
	}

	return &lessThanMatcher{v}
}

type lessThanMatcher struct {
	limit reflect.Value
}

func (m *lessThanMatcher) Description() string {
	// Special case: make it clear that strings are strings.
	if m.limit.Kind() == reflect.String {
		return fmt.Sprintf("less than \"%s\"", m.limit.String())
	}

	return fmt.Sprintf("less than %v", m.limit.Interface())
}

func compareIntegers(v1, v2 reflect.Value) (res MatchResult, err error) {
	res = MATCH_FALSE
	switch {
	case isSignedInteger(v1) && isSignedInteger(v2):
		if v1.Int() < v2.Int() {
			res = MATCH_TRUE
		}
		return

	case isSignedInteger(v1) && isUnsignedInteger(v2):
		if v1.Int() < 0 || uint64(v1.Int()) < v2.Uint() {
			res = MATCH_TRUE
		}
		return

	case isUnsignedInteger(v1) && isSignedInteger(v2):
		if v1.Uint() <= math.MaxInt64 && int64(v1.Uint()) < v2.Int() {
			res = MATCH_TRUE
		}
		return

	case isUnsignedInteger(v1) && isUnsignedInteger(v2):
		if v1.Uint() < v2.Uint() {
			res = MATCH_TRUE
		}
		return
	}

	panic(fmt.Sprintf("compareIntegers: %v %v", v1, v2))
}

func getFloat(v reflect.Value) float64 {
	switch {
	case isSignedInteger(v):
		return float64(v.Int())

	case isUnsignedInteger(v):
		return float64(v.Uint())

	case isFloat(v):
		return v.Float()
	}

	panic(fmt.Sprintf("getFloat: %v", v))
}

func (m *lessThanMatcher) Matches(c interface{}) (res MatchResult, err error) {
	v1 := reflect.ValueOf(c)
	v2 := m.limit

	res = MATCH_FALSE

	// Handle strings as a special case.
	if v1.Kind() == reflect.String && v2.Kind() == reflect.String {
		if v1.String() < v2.String() {
			res = MATCH_TRUE
		}
		return
	}

	// If we get here, we require that we are dealing with integers or floats.
	v1Legal := isInteger(v1) || isFloat(v1)
	v2Legal := isInteger(v2) || isFloat(v2)
	if !v1Legal || !v2Legal {
		res = MATCH_UNDEFINED
		err = errors.New("which is not comparable")
		return
	}

	// Handle the various comparison cases.
	switch {
	// Both integers
	case isInteger(v1) && isInteger(v2):
		return compareIntegers(v1, v2)

	// At least one float32
	case v1.Kind() == reflect.Float32 || v2.Kind() == reflect.Float32:
		if float32(getFloat(v1)) < float32(getFloat(v2)) {
			res = MATCH_TRUE
		}
		return

	// At least one float64
	case v1.Kind() == reflect.Float64 || v2.Kind() == reflect.Float64:
		if getFloat(v1) < getFloat(v2) {
			res = MATCH_TRUE
		}
		return
	}

	// We shouldn't get here.
	panic(fmt.Sprintf("lessThanMatcher.Matches: Shouldn't get here: %v %v", v1, v2))
}
<commit_msg>Fixed up less_than.go for #20.<commit_after>// Copyright 2011 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package oglematchers

import (
	"errors"
	"fmt"
	"math"
	"reflect"
)

// LessThan returns a matcher that matches integer, floating point, or strings
// values v such that v < x. Comparison is not defined between numeric and
// string types, but is defined between all integer and floating point types.
//
// x must itself be an integer, floating point, or string type; otherwise,
// LessThan will panic.
func LessThan(x interface{}) Matcher {
	v := reflect.ValueOf(x)
	kind := v.Kind()

	switch {
	case isInteger(v):
	case isFloat(v):
	case kind == reflect.String:

	default:
		panic(fmt.Sprintf("LessThan: unexpected kind %v", kind))
	}

	return &lessThanMatcher{v}
}

type lessThanMatcher struct {
	limit reflect.Value
}

func (m *lessThanMatcher) Description() string {
	// Special case: make it clear that strings are strings.
	if m.limit.Kind() == reflect.String {
		return fmt.Sprintf("less than \"%s\"", m.limit.String())
	}

	return fmt.Sprintf("less than %v", m.limit.Interface())
}

func compareIntegers(v1, v2 reflect.Value) (res bool, err error) {
	res = false
	err = errors.New("")

	switch {
	case isSignedInteger(v1) && isSignedInteger(v2):
		if v1.Int() < v2.Int() {
			res = true
			err = nil
		}
		return

	case isSignedInteger(v1) && isUnsignedInteger(v2):
		if v1.Int() < 0 || uint64(v1.Int()) < v2.Uint() {
			res = true
			err = nil
		}
		return

	case isUnsignedInteger(v1) && isSignedInteger(v2):
		if v1.Uint() <= math.MaxInt64 && int64(v1.Uint()) < v2.Int() {
			res = true
			err = nil
		}
		return

	case isUnsignedInteger(v1) && isUnsignedInteger(v2):
		if v1.Uint() < v2.Uint() {
			res = true
			err = nil
		}
		return
	}

	panic(fmt.Sprintf("compareIntegers: %v %v", v1, v2))
}

func getFloat(v reflect.Value) float64 {
	switch {
	case isSignedInteger(v):
		return float64(v.Int())

	case isUnsignedInteger(v):
		return float64(v.Uint())

	case isFloat(v):
		return v.Float()
	}

	panic(fmt.Sprintf("getFloat: %v", v))
}

func (m *lessThanMatcher) Matches(c interface{}) (res bool, err error) {
	v1 := reflect.ValueOf(c)
	v2 := m.limit

	res = false
	err = errors.New("")

	// Handle strings as a special case.
	if v1.Kind() == reflect.String && v2.Kind() == reflect.String {
		if v1.String() < v2.String() {
			res = true
			err = nil
		}
		return
	}

	// If we get here, we require that we are dealing with integers or floats.
	v1Legal := isInteger(v1) || isFloat(v1)
	v2Legal := isInteger(v2) || isFloat(v2)
	if !v1Legal || !v2Legal {
		res = false
		err = NewFatalError("which is not comparable")
		return
	}

	// Handle the various comparison cases.
	switch {
	// Both integers
	case isInteger(v1) && isInteger(v2):
		return compareIntegers(v1, v2)

	// At least one float32
	case v1.Kind() == reflect.Float32 || v2.Kind() == reflect.Float32:
		if float32(getFloat(v1)) < float32(getFloat(v2)) {
			res = true
			err = nil
		}
		return

	// At least one float64
	case v1.Kind() == reflect.Float64 || v2.Kind() == reflect.Float64:
		if getFloat(v1) < getFloat(v2) {
			res = true
			err = nil
		}
		return
	}

	// We shouldn't get here.
	panic(fmt.Sprintf("lessThanMatcher.Matches: Shouldn't get here: %v %v", v1, v2))
}
<|endoftext|>


<commit_before>package git

import (
	"bytes"
	"errors"
	"fmt"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"
)

const (
	// GitCommandDateLayout corresponds to `git log --date=format:%FT%T%z` date format.
	GitCommandDateLayout = "2006-01-02T15:04:05-0700"

	gitPrettyFormat = "%H\n%an\n%cn\n%cd\n%s"
	gitDateFormat   = "format:%FT%T%z"
)

var (
	gitPrettyFormatFieldsNum = strings.Count(gitPrettyFormat, "\n") + 1
	errUnexpectedExit        = errors.New("unexpected exit")
)

type systemGit string

// SystemGit returns an object that wraps system git command and implements git.Command interface.
// If git is not found in PATH an error will be returned.
func SystemGit() (systemGit, error) {
	cmd, err := exec.LookPath("git")
	if err != nil {
		return systemGit(""), errors.New("git is not found in PATH")
	}

	return systemGit(cmd), nil
}

// Exec runs specified Git command in path and returns its output. If Git returns a non-zero
// status, an error is returned and output contains error details.
func (gitCmd systemGit) Exec(path, command string, args ...string) (output []byte, err error) {
	cmd := exec.Command(string(gitCmd), append([]string{command}, args...)...)
	cmd.Dir = path

	output, err = cmd.Output()
	if err != nil {
		if exitErr, ok := err.(*exec.ExitError); ok {
			err = errors.New(string(exitErr.Stderr))
		} else {
			err = errUnexpectedExit
		}

		return nil, err
	}

	return bytes.TrimSpace(output), nil
}

// IsRepository checks if there if `path` is a git repository.
func (gitCmd systemGit) IsRepository(path string) bool {
	if fileInfo, err := os.Stat(path); err != nil {
		if os.IsNotExist(err) {
			return false
		}

		log.Printf("[WARN] failed to stat %s (%s)", path, err)
		return false
	} else if !fileInfo.IsDir() {
		return false
	}

	output, err := gitCmd.Exec(path, "rev-parse", "--is-inside-work-tree")
	if err == errUnexpectedExit {
		log.Printf("[WARN] git rev-parse --is-inside-work-tree returned %s for %s (%s)", err, path, string(output))
	} else if err != nil {
		return false
	}

	switch string(output) {
	case "true":
		return true
	case "false":
		return false
	default:
		log.Printf("[WARN] git rev-parse --is-inside-work-tree returned unexpected output for %s: %q", path, string(output))
		return false
	}
}

// CurrentBranch returns the name of current branch in `path`.
func (gitCmd systemGit) CurrentBranch(path string) string {
	refName, err := gitCmd.Exec(path, "symbolic-ref", "HEAD")
	if err != nil {
		log.Printf("[WARN] git symbolic-ref HEAD returned %s for %s (%s)", err, path, string(refName))
		return DefaultMaster
	}

	if !bytes.HasPrefix(refName, []byte("refs/heads/")) {
		log.Printf("[WARN] unexpected reference name for %s (%q)", path, refName)
		return DefaultMaster
	}

	return string(bytes.TrimPrefix(refName, []byte("refs/heads/")))
}

// LastCommit returns the latest commit from `path`.
func (gitCmd systemGit) LastCommit(path string) (commit Commit, err error) {
	output, err := gitCmd.Exec(path, "log", "-n", "1", "--pretty="+gitPrettyFormat, "--date="+gitDateFormat)
	if err != nil {
		log.Printf("[WARN] git log returned error %s for %s (%s)", err, path, string(output))
		return commit, nil
	}

	lines := strings.SplitN(string(output), "\n", gitPrettyFormatFieldsNum)
	if len(lines) < gitPrettyFormatFieldsNum {
		log.Printf("[WARN] unexpected output from git log for %s (%s)", path, string(output))
		return commit, nil
	}

	commit.SHA, commit.Author, commit.Committer, commit.Message = lines[0], lines[1], lines[2], lines[4]
	commit.Date, err = time.Parse(GitCommandDateLayout, lines[3])
	if err != nil {
		log.Printf("[WARN] unexpected date format from git log for %s (%s)", path, lines[3])
		commit.Date = time.Time{}
	}

	return commit, nil
}

// CloneMirror performs mirror clone of specified git URL to `path`.
func (gitCmd systemGit) CloneMirror(gitURL, path string) error {
	dir, projectName := filepath.Dir(path), filepath.Base(path)

	if err := os.MkdirAll(dir, 0755); err != nil {
		log.Printf("failed to create %s (%s)", dir, err)
		return fmt.Errorf("failed to clone %s to %s", gitURL, path)
	}

	output, err := gitCmd.Exec(dir, "clone", "--mirror", gitURL, projectName)
	if err != nil {
		log.Printf("git clone --mirror %s to %s returned %s (%s)", gitURL, path, err, string(output))
		return fmt.Errorf("failed to clone %s to %s", gitURL, path)
	}

	return nil
}

// UpdateRemote does `git remote update` in specified `path`.
func (gitCmd systemGit) UpdateRemote(path string) error {
	output, err := gitCmd.Exec(path, "remote", "update")
	if err != nil {
		log.Printf("[WARN] git remote update returned %s for %s (%s)", err, path, string(output))
		return errors.New("update failed")
	}

	return nil
}
<commit_msg>Revert "Use `git rev-parse --is-inside-work-tree` to check if dir is a repository"<commit_after>package git

import (
	"bytes"
	"errors"
	"fmt"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"
)

const (
	// GitCommandDateLayout corresponds to `git log --date=format:%FT%T%z` date format.
	GitCommandDateLayout = "2006-01-02T15:04:05-0700"

	gitPrettyFormat = "%H\n%an\n%cn\n%cd\n%s"
	gitDateFormat   = "format:%FT%T%z"
)

var (
	gitPrettyFormatFieldsNum = strings.Count(gitPrettyFormat, "\n") + 1
	errUnexpectedExit        = errors.New("unexpected exit")
)

type systemGit string

// SystemGit returns an object that wraps system git command and implements git.Command interface.
// If git is not found in PATH an error will be returned.
func SystemGit() (systemGit, error) {
	cmd, err := exec.LookPath("git")
	if err != nil {
		return systemGit(""), errors.New("git is not found in PATH")
	}

	return systemGit(cmd), nil
}

// Exec runs specified Git command in path and returns its output. If Git returns a non-zero
// status, an error is returned and output contains error details.
func (gitCmd systemGit) Exec(path, command string, args ...string) (output []byte, err error) {
	cmd := exec.Command(string(gitCmd), append([]string{command}, args...)...)
	cmd.Dir = path

	output, err = cmd.Output()
	if err != nil {
		if exitErr, ok := err.(*exec.ExitError); ok {
			err = errors.New(string(exitErr.Stderr))
		} else {
			err = errUnexpectedExit
		}

		return nil, err
	}

	return bytes.TrimSpace(output), nil
}

// IsRepository checks if there if `path` is a git repository.
func (gitCmd systemGit) IsRepository(path string) bool {
	if fileInfo, err := os.Stat(path); err != nil {
		if os.IsNotExist(err) {
			return false
		}

		log.Printf("[WARN] failed to stat %s (%s)", path, err)
		return false
	} else if !fileInfo.IsDir() {
		return false
	}

	output, err := gitCmd.Exec(path, "rev-parse", "--is-inside-git-dir")
	if err == errUnexpectedExit {
		log.Printf("[WARN] git rev-parse --is-inside-git-dir returned %s for %s (%s)", err, path, string(output))
	} else if err != nil {
		return false
	}

	switch string(output) {
	case "true":
		return true
	case "false":
		return false
	default:
		log.Printf("[WARN] git rev-parse --is-inside-git-dir returned unexpected output for %s: %q", path, string(output))
		return false
	}
}

// CurrentBranch returns the name of current branch in `path`.
func (gitCmd systemGit) CurrentBranch(path string) string {
	refName, err := gitCmd.Exec(path, "symbolic-ref", "HEAD")
	if err != nil {
		log.Printf("[WARN] git symbolic-ref HEAD returned %s for %s (%s)", err, path, string(refName))
		return DefaultMaster
	}

	if !bytes.HasPrefix(refName, []byte("refs/heads/")) {
		log.Printf("[WARN] unexpected reference name for %s (%q)", path, refName)
		return DefaultMaster
	}

	return string(bytes.TrimPrefix(refName, []byte("refs/heads/")))
}

// LastCommit returns the latest commit from `path`.
func (gitCmd systemGit) LastCommit(path string) (commit Commit, err error) {
	output, err := gitCmd.Exec(path, "log", "-n", "1", "--pretty="+gitPrettyFormat, "--date="+gitDateFormat)
	if err != nil {
		log.Printf("[WARN] git log returned error %s for %s (%s)", err, path, string(output))
		return commit, nil
	}

	lines := strings.SplitN(string(output), "\n", gitPrettyFormatFieldsNum)
	if len(lines) < gitPrettyFormatFieldsNum {
		log.Printf("[WARN] unexpected output from git log for %s (%s)", path, string(output))
		return commit, nil
	}

	commit.SHA, commit.Author, commit.Committer, commit.Message = lines[0], lines[1], lines[2], lines[4]
	commit.Date, err = time.Parse(GitCommandDateLayout, lines[3])
	if err != nil {
		log.Printf("[WARN] unexpected date format from git log for %s (%s)", path, lines[3])
		commit.Date = time.Time{}
	}

	return commit, nil
}

// CloneMirror performs mirror clone of specified git URL to `path`.
func (gitCmd systemGit) CloneMirror(gitURL, path string) error {
	dir, projectName := filepath.Dir(path), filepath.Base(path)

	if err := os.MkdirAll(dir, 0755); err != nil {
		log.Printf("failed to create %s (%s)", dir, err)
		return fmt.Errorf("failed to clone %s to %s", gitURL, path)
	}

	output, err := gitCmd.Exec(dir, "clone", "--mirror", gitURL, projectName)
	if err != nil {
		log.Printf("git clone --mirror %s to %s returned %s (%s)", gitURL, path, err, string(output))
		return fmt.Errorf("failed to clone %s to %s", gitURL, path)
	}

	return nil
}

// UpdateRemote does `git remote update` in specified `path`.
func (gitCmd systemGit) UpdateRemote(path string) error {
	output, err := gitCmd.Exec(path, "remote", "update")
	if err != nil {
		log.Printf("[WARN] git remote update returned %s for %s (%s)", err, path, string(output))
		return errors.New("update failed")
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"github.com/rlister/let-me-in/Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws"
	"github.com/rlister/let-me-in/Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/rlister/let-me-in/Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/ec2"
	"io/ioutil"
	"net/http"
	"os"
	"os/exec"
	"text/tabwriter"
)

var VERSION = "dev"

type LmiInput struct {
	GroupId    *string
	IpProtocol *string
	FromPort   *int64
	ToPort     *int64
	CidrIp     *string
}

// error handler
func check(e error) {
	if e != nil {
		panic(e.Error())
	}
}

// return array of security group objects for given group names
func getGroups(client *ec2.EC2, names []string) ([]*ec2.SecurityGroup, error) {

	// get names as array of aws.String objects
	values := make([]*string, len(names))
	for i, name := range names {
		values[i] = aws.String(name)
	}

	// request params as filter for names
	params := &ec2.DescribeSecurityGroupsInput{
		Filters: []*ec2.Filter{
			{
				Name:   aws.String("group-name"),
				Values: values,
			},
		},
	}

	// send request
	resp, err := client.DescribeSecurityGroups(params)
	if err != nil {
		return nil, err
	}

	return resp.SecurityGroups, nil
}

func authorizeGroup(client *ec2.EC2, group *ec2.SecurityGroup, input LmiInput) {
	_, err := client.AuthorizeSecurityGroupIngress(&ec2.AuthorizeSecurityGroupIngressInput{
		GroupId:    group.GroupId,
		IpProtocol: input.IpProtocol,
		FromPort:   input.FromPort,
		ToPort:     input.ToPort,
		CidrIp:     input.CidrIp,
	})

	// be idempotent, i.e. skip error if this permission already exists in group
	if err != nil {
		if err.(awserr.Error).Code() != "InvalidPermission.Duplicate" {
			panic(err)
		}
	}
}

func revokeGroup(client *ec2.EC2, group *ec2.SecurityGroup, input LmiInput) {
	_, err := client.RevokeSecurityGroupIngress(&ec2.RevokeSecurityGroupIngressInput{
		GroupId:    group.GroupId,
		IpProtocol: input.IpProtocol,
		FromPort:   input.FromPort,
		ToPort:     input.ToPort,
		CidrIp:     input.CidrIp,
	})

	// be idempotent, i.e. skip error if this permission already exists in group
	if err != nil {
		if err.(awserr.Error).Code() != "InvalidPermission.NotFound" {
			panic(err)
		}
	}
}

// revoke all existing permissions for security group
func cleanGroup(client *ec2.EC2, group *ec2.SecurityGroup) {
	for _, perm := range group.IpPermissions {
		for _, cidr := range perm.IpRanges {
			revokeGroup(client, group, LmiInput{
				IpProtocol: perm.IpProtocol,
				FromPort:   perm.FromPort,
				ToPort:     perm.ToPort,
				CidrIp:     cidr.CidrIp,
			})
		}
	}
}

// get my external-facing IP as a string
func getMyIp(ident string) string {
	resp, err := http.Get(ident)
	check(err)
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	check(err)

	return string(body)
}

// parse cmdline argv into args before '--' and cmd to exec afterwards
func parseArgs(argv []string) ([]string, []string) {

	// if -- found, return slices before and after
	for i, arg := range flag.Args() {
		if arg == "--" {
			return argv[0:i], argv[i+1:]
		}
	}

	// no -- found, return all of argv
	return argv, nil
}

// print out table of current IP permissions for given security groups
func printIpRanges(groups []*ec2.SecurityGroup) {
	w := new(tabwriter.Writer)
	w.Init(os.Stdout, 0, 8, 0, '\t', 0)
	for _, group := range groups {
		for _, perm := range group.IpPermissions {
			for _, cidr := range perm.IpRanges {
				fmt.Fprintf(w, "%v\t%v\t%v\t%v\n", *group.GroupName, *perm.IpProtocol, *cidr.CidrIp, *perm.FromPort)
			}
		}
	}
	w.Flush()
}

func main() {

	// cmdline options
	versionFlag := flag.Bool("v", false, "show version and exit")
	cidr := flag.String("c", "", "set a specific cidr block (default: current public ip)")
	protocol := flag.String("P", "TCP", "protocol to allow (default: TCP)")
	port := flag.Int("p", 22, "port number to allow (default: 22)")
	revoke := flag.Bool("r", false, "revoke access from security groups (default: false)")
	cleanFlag := flag.Bool("clean", false, "clean listed groups, i.e. revoke all access")
	list := flag.Bool("l", false, "list current rules for groups")
	flag.Parse()

	// show version and exit
	if *versionFlag {
		fmt.Printf("let-me-in %v\n", VERSION)
		return
	}

	// if cidr not given get ip from external service
	if *cidr == "" {
		ident := os.Getenv("LMI_IDENT_URL")
		if ident == "" {
			ident = "http://v4.ident.me/"
		}
		ip := getMyIp(ident) + "/32"
		cidr = &ip
	}

	input := LmiInput{
		IpProtocol: protocol,
		FromPort:   aws.Int64(int64(*port)),
		ToPort:     aws.Int64(int64(*port)),
		CidrIp:     cidr,
	}

	// configure aws-sdk from AWS_* env vars
	client := ec2.New(&aws.Config{})

	// get security group names and any command to exec after '--'
	groupNames, cmd := parseArgs(flag.Args())

	// get details for listed groups
	groups, err := getGroups(client, groupNames)
	if err != nil {
		fmt.Printf("%v\n", err) // if AWS creds not configured, report it here
		return
	}

	// print list of current IP permissions for groups
	if *list {
		printIpRanges(groups)
		return
	}

	if *cleanFlag {
		for _, group := range groups {
			cleanGroup(client, group)
		}
		return
	}

	// revoke on -r option
	if *revoke {
		for _, group := range groups {
			revokeGroup(client, group, input)
		}
		return
	}

	// default behaviour
	for _, group := range groups {
		authorizeGroup(client, group, input)
	}

	// exec any command after '--', then revoke
	if cmd != nil {
		c := exec.Command(cmd[0], cmd[1:]...)
		c.Stdout = os.Stdout
		c.Stdin = os.Stdin
		c.Stderr = os.Stderr

		err := c.Run()
		if err != nil {
			fmt.Println(err) // show err and keep running so we hit revoke below
		}

		for _, group := range groups {
			revokeGroup(client, group, input)
		}
	}

}
<commit_msg>add convenience functions to call multiple groups<commit_after>package main

import (
	"flag"
	"fmt"
	"github.com/rlister/let-me-in/Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws"
	"github.com/rlister/let-me-in/Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/rlister/let-me-in/Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/ec2"
	"io/ioutil"
	"net/http"
	"os"
	"os/exec"
	"text/tabwriter"
)

var VERSION = "dev"

type LmiInput struct {
	GroupId    *string
	IpProtocol *string
	FromPort   *int64
	ToPort     *int64
	CidrIp     *string
}

// error handler
func check(e error) {
	if e != nil {
		panic(e.Error())
	}
}

// return array of security group objects for given group names
func getGroups(client *ec2.EC2, names []string) ([]*ec2.SecurityGroup, error) {

	// get names as array of aws.String objects
	values := make([]*string, len(names))
	for i, name := range names {
		values[i] = aws.String(name)
	}

	// request params as filter for names
	params := &ec2.DescribeSecurityGroupsInput{
		Filters: []*ec2.Filter{
			{
				Name:   aws.String("group-name"),
				Values: values,
			},
		},
	}

	// send request
	resp, err := client.DescribeSecurityGroups(params)
	if err != nil {
		return nil, err
	}

	return resp.SecurityGroups, nil
}

func authorizeGroups(client *ec2.EC2, groups []*ec2.SecurityGroup, input LmiInput) {
	for _, group := range groups {
		authorizeGroup(client, group, input)
	}
}

// add given permission to security group
func authorizeGroup(client *ec2.EC2, group *ec2.SecurityGroup, input LmiInput) {
	_, err := client.AuthorizeSecurityGroupIngress(&ec2.AuthorizeSecurityGroupIngressInput{
		GroupId:    group.GroupId,
		IpProtocol: input.IpProtocol,
		FromPort:   input.FromPort,
		ToPort:     input.ToPort,
		CidrIp:     input.CidrIp,
	})

	// be idempotent, i.e. skip error if this permission already exists in group
	if err != nil {
		if err.(awserr.Error).Code() != "InvalidPermission.Duplicate" {
			panic(err)
		}
	}
}

func revokeGroups(client *ec2.EC2, groups []*ec2.SecurityGroup, input LmiInput) {
	for _, group := range groups {
		revokeGroup(client, group, input)
	}
}

// revoke given permission for security group
func revokeGroup(client *ec2.EC2, group *ec2.SecurityGroup, input LmiInput) {
	_, err := client.RevokeSecurityGroupIngress(&ec2.RevokeSecurityGroupIngressInput{
		GroupId:    group.GroupId,
		IpProtocol: input.IpProtocol,
		FromPort:   input.FromPort,
		ToPort:     input.ToPort,
		CidrIp:     input.CidrIp,
	})

	// be idempotent, i.e. skip error if this permission already exists in group
	if err != nil {
		if err.(awserr.Error).Code() != "InvalidPermission.NotFound" {
			panic(err)
		}
	}
}

func cleanGroups(client *ec2.EC2, groups []*ec2.SecurityGroup) {
	for _, group := range groups {
		cleanGroup(client, group)
	}
}

// revoke all existing permissions for security group
func cleanGroup(client *ec2.EC2, group *ec2.SecurityGroup) {
	for _, perm := range group.IpPermissions {
		for _, cidr := range perm.IpRanges {
			revokeGroup(client, group, LmiInput{
				IpProtocol: perm.IpProtocol,
				FromPort:   perm.FromPort,
				ToPort:     perm.ToPort,
				CidrIp:     cidr.CidrIp,
			})
		}
	}
}

// get my external-facing IP as a string
func getMyIp(ident string) string {
	resp, err := http.Get(ident)
	check(err)
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	check(err)

	return string(body)
}

// parse cmdline argv into args before '--' and cmd to exec afterwards
func parseArgs(argv []string) ([]string, []string) {

	// if -- found, return slices before and after
	for i, arg := range flag.Args() {
		if arg == "--" {
			return argv[0:i], argv[i+1:]
		}
	}

	// no -- found, return all of argv
	return argv, nil
}

// print out table of current IP permissions for given security groups
func printIpRanges(groups []*ec2.SecurityGroup) {
	w := new(tabwriter.Writer)
	w.Init(os.Stdout, 0, 8, 0, '\t', 0)
	for _, group := range groups {
		for _, perm := range group.IpPermissions {
			for _, cidr := range perm.IpRanges {
				fmt.Fprintf(w, "%v\t%v\t%v\t%v\n", *group.GroupName, *perm.IpProtocol, *cidr.CidrIp, *perm.FromPort)
			}
		}
	}
	w.Flush()
}

func main() {

	// cmdline options
	versionFlag := flag.Bool("v", false, "show version and exit")
	cidr := flag.String("c", "", "set a specific cidr block (default: current public ip)")
	protocol := flag.String("P", "TCP", "protocol to allow (default: TCP)")
	port := flag.Int("p", 22, "port number to allow (default: 22)")
	revoke := flag.Bool("r", false, "revoke access from security groups (default: false)")
	cleanFlag := flag.Bool("clean", false, "clean listed groups, i.e. revoke all access")
	list := flag.Bool("l", false, "list current rules for groups")
	flag.Parse()

	// show version and exit
	if *versionFlag {
		fmt.Printf("let-me-in %v\n", VERSION)
		return
	}

	// if cidr not given get ip from external service
	if *cidr == "" {
		ident := os.Getenv("LMI_IDENT_URL")
		if ident == "" {
			ident = "http://v4.ident.me/"
		}
		ip := getMyIp(ident) + "/32"
		cidr = &ip
	}

	input := LmiInput{
		IpProtocol: protocol,
		FromPort:   aws.Int64(int64(*port)),
		ToPort:     aws.Int64(int64(*port)),
		CidrIp:     cidr,
	}

	// configure aws-sdk from AWS_* env vars
	client := ec2.New(&aws.Config{})

	// get security group names and any command to exec after '--'
	groupNames, cmd := parseArgs(flag.Args())

	// get details for listed groups
	groups, err := getGroups(client, groupNames)
	if err != nil {
		fmt.Printf("%v\n", err) // if AWS creds not configured, report it here
		return
	}

	// print list of current IP permissions for groups
	if *list {
		printIpRanges(groups)
		return
	}

	// remove all existing permissions for groups
	if *cleanFlag {
		cleanGroups(client, groups)
		return
	}

	// revoke given permission for groups
	if *revoke {
		revokeGroups(client, groups, input)
		return
	}

	// default behaviour
	authorizeGroups(client, groups, input)

	// exec any command after '--', then revoke
	if cmd != nil {
		c := exec.Command(cmd[0], cmd[1:]...)
		c.Stdout = os.Stdout
		c.Stdin = os.Stdin
		c.Stderr = os.Stderr

		err := c.Run()
		if err != nil {
			fmt.Println(err) // show err and keep running so we hit revoke below
		}

		revokeGroups(client, groups, input)
	}
}
<|endoftext|>


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

// A package of simple functions to manipulate strings.
package strings

import (
	"unicode"
	"utf8"
)

// explode splits s into an array of UTF-8 sequences, one per Unicode character (still strings) up to a maximum of n (n <= 0 means no limit).
// Invalid UTF-8 sequences become correct encodings of U+FFF8.
func explode(s string, n int) []string {
	if n <= 0 {
		n = len(s)
	}
	a := make([]string, n)
	var size, rune int
	na := 0
	for len(s) > 0 {
		if na+1 >= n {
			a[na] = s
			na++
			break
		}
		rune, size = utf8.DecodeRuneInString(s)
		s = s[size:]
		a[na] = string(rune)
		na++
	}
	return a[0:na]
}

// Count counts the number of non-overlapping instances of sep in s.
func Count(s, sep string) int {
	if sep == "" {
		return utf8.RuneCountInString(s) + 1
	}
	c := sep[0]
	n := 0
	for i := 0; i+len(sep) <= len(s); i++ {
		if s[i] == c && (len(sep) == 1 || s[i:i+len(sep)] == sep) {
			n++
			i += len(sep) - 1
		}
	}
	return n
}

// Index returns the index of the first instance of sep in s, or -1 if sep is not present in s.
func Index(s, sep string) int {
	n := len(sep)
	if n == 0 {
		return 0
	}
	c := sep[0]
	if n == 1 {
		// special case worth making fast
		for i := 0; i < len(s); i++ {
			if s[i] == c {
				return i
			}
		}
		return -1
	}
	for i := 0; i+n <= len(s); i++ {
		if s[i] == c && (n == 1 || s[i:i+n] == sep) {
			return i
		}
	}
	return -1
}

// LastIndex returns the index of the last instance of sep in s, or -1 if sep is not present in s.
func LastIndex(s, sep string) int {
	n := len(sep)
	if n == 0 {
		return len(s)
	}
	c := sep[0]
	if n == 1 {
		// special case worth making fast
		for i := len(s) - 1; i >= 0; i-- {
			if s[i] == c {
				return i
			}
		}
		return -1
	}
	for i := len(s) - n; i >= 0; i-- {
		if s[i] == c && (n == 1 || s[i:i+n] == sep) {
			return i
		}
	}
	return -1
}

// Generic split: splits after each instance of sep,
// including sepSave bytes of sep in the subarrays.
func genSplit(s, sep string, sepSave, n int) []string {
	if sep == "" {
		return explode(s, n)
	}
	if n <= 0 {
		n = Count(s, sep) + 1
	}
	c := sep[0]
	start := 0
	a := make([]string, n)
	na := 0
	for i := 0; i+len(sep) <= len(s) && na+1 < n; i++ {
		if s[i] == c && (len(sep) == 1 || s[i:i+len(sep)] == sep) {
			a[na] = s[start : i+sepSave]
			na++
			start = i + len(sep)
			i += len(sep) - 1
		}
	}
	a[na] = s[start:]
	return a[0 : na+1]
}

// Split splits the string s around each instance of sep, returning an array of substrings of s.
// If sep is empty, Split splits s after each UTF-8 sequence.
// If n > 0, Split splits s into at most n substrings; the last substring will be the unsplit remainder.
func Split(s, sep string, n int) []string { return genSplit(s, sep, 0, n) }

// SplitAfter splits the string s after each instance of sep, returning an array of substrings of s.
// If sep is empty, SplitAfter splits s after each UTF-8 sequence.
// If n > 0, SplitAfter splits s into at most n substrings; the last substring will be the unsplit remainder.
func SplitAfter(s, sep string, n int) []string {
	return genSplit(s, sep, len(sep), n)
}

// Fields splits the string s around each instance of one or more consecutive white space
// characters, returning an array of substrings of s or an empty list if s contains only white space.
func Fields(s string) []string {
	n := 0
	inField := false
	for _, rune := range s {
		wasInField := inField
		inField = !unicode.IsSpace(rune)
		if inField && !wasInField {
			n++
		}
	}

	a := make([]string, n)
	na := 0
	fieldStart := -1
	for i, rune := range s {
		if unicode.IsSpace(rune) {
			if fieldStart >= 0 {
				a[na] = s[fieldStart:i]
				na++
				fieldStart = -1
			}
		} else if fieldStart == -1 {
			fieldStart = i
		}
	}
	if fieldStart != -1 {
		a[na] = s[fieldStart:]
		na++
	}
	return a[0:na]
}

// Join concatenates the elements of a to create a single string.   The separator string
// sep is placed between elements in the resulting string.
func Join(a []string, sep string) string {
	if len(a) == 0 {
		return ""
	}
	if len(a) == 1 {
		return a[0]
	}
	n := len(sep) * (len(a) - 1)
	for i := 0; i < len(a); i++ {
		n += len(a[i])
	}

	b := make([]byte, n)
	bp := 0
	for i := 0; i < len(a); i++ {
		s := a[i]
		for j := 0; j < len(s); j++ {
			b[bp] = s[j]
			bp++
		}
		if i+1 < len(a) {
			s = sep
			for j := 0; j < len(s); j++ {
				b[bp] = s[j]
				bp++
			}
		}
	}
	return string(b)
}

// HasPrefix tests whether the string s begins with prefix.
func HasPrefix(s, prefix string) bool {
	return len(s) >= len(prefix) && s[0:len(prefix)] == prefix
}

// HasSuffix tests whether the string s ends with suffix.
func HasSuffix(s, suffix string) bool {
	return len(s) >= len(suffix) && s[len(s)-len(suffix):] == suffix
}

// Map returns a copy of the string s with all its characters modified
// according to the mapping function. If mapping returns a negative value, the character is
// dropped from the string with no replacement.
func Map(mapping func(rune int) int, s string) string {
	// In the worst case, the string can grow when mapped, making
	// things unpleasant.  But it's so rare we barge in assuming it's
	// fine.  It could also shrink but that falls out naturally.
	maxbytes := len(s) // length of b
	nbytes := 0        // number of bytes encoded in b
	b := make([]byte, maxbytes)
	for _, c := range s {
		rune := mapping(c)
		if rune >= 0 {
			wid := 1
			if rune >= utf8.RuneSelf {
				wid = utf8.RuneLen(rune)
			}
			if nbytes+wid > maxbytes {
				// Grow the buffer.
				maxbytes = maxbytes*2 + utf8.UTFMax
				nb := make([]byte, maxbytes)
				for i, c := range b[0:nbytes] {
					nb[i] = c
				}
				b = nb
			}
			nbytes += utf8.EncodeRune(rune, b[nbytes:maxbytes])
		}
	}
	return string(b[0:nbytes])
}

// Repeat returns a new string consisting of count copies of the string s.
func Repeat(s string, count int) string {
	b := make([]byte, len(s)*count)
	bp := 0
	for i := 0; i < count; i++ {
		for j := 0; j < len(s); j++ {
			b[bp] = s[j]
			bp++
		}
	}
	return string(b)
}

// ToUpper returns a copy of the string s with all Unicode letters mapped to their upper case.
func ToUpper(s string) string { return Map(unicode.ToUpper, s) }

// ToLower returns a copy of the string s with all Unicode letters mapped to their lower case.
func ToLower(s string) string { return Map(unicode.ToLower, s) }

// ToTitle returns a copy of the string s with all Unicode letters mapped to their title case.
func ToTitle(s string) string { return Map(unicode.ToTitle, s) }

// Trim returns a slice of the string s, with all leading and trailing white space
// removed, as defined by Unicode.
func TrimSpace(s string) string {
	start, end := 0, len(s)
	for start < end {
		wid := 1
		rune := int(s[start])
		if rune >= utf8.RuneSelf {
			rune, wid = utf8.DecodeRuneInString(s[start:end])
		}
		if !unicode.IsSpace(rune) {
			break
		}
		start += wid
	}
	for start < end {
		wid := 1
		rune := int(s[end-1])
		if rune >= utf8.RuneSelf {
			// Back up carefully looking for beginning of rune. Mustn't pass start.
			for wid = 2; start <= end-wid && !utf8.RuneStart(s[end-wid]); wid++ {
			}
			if start > end-wid { // invalid UTF-8 sequence; stop processing
				return s[start:end]
			}
			rune, wid = utf8.DecodeRuneInString(s[end-wid : end])
		}
		if !unicode.IsSpace(rune) {
			break
		}
		end -= wid
	}
	return s[start:end]
}

// Bytes returns a new slice containing the bytes in s.
func Bytes(s string) []byte {
	b := make([]byte, len(s))
	for i := 0; i < len(s); i++ {
		b[i] = s[i]
	}
	return b
}

// Runes returns a slice of runes (Unicode code points) equivalent to the string s.
func Runes(s string) []int {
	t := make([]int, utf8.RuneCountInString(s))
	i := 0
	for _, r := range s {
		t[i] = r
		i++
	}
	return t
}
<commit_msg>strings: remove a couple of redundant tests (per suggestion from Heresy.Mc@gmail.com)<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// A package of simple functions to manipulate strings.
package strings

import (
	"unicode"
	"utf8"
)

// explode splits s into an array of UTF-8 sequences, one per Unicode character (still strings) up to a maximum of n (n <= 0 means no limit).
// Invalid UTF-8 sequences become correct encodings of U+FFF8.
func explode(s string, n int) []string {
	if n <= 0 {
		n = len(s)
	}
	a := make([]string, n)
	var size, rune int
	na := 0
	for len(s) > 0 {
		if na+1 >= n {
			a[na] = s
			na++
			break
		}
		rune, size = utf8.DecodeRuneInString(s)
		s = s[size:]
		a[na] = string(rune)
		na++
	}
	return a[0:na]
}

// Count counts the number of non-overlapping instances of sep in s.
func Count(s, sep string) int {
	if sep == "" {
		return utf8.RuneCountInString(s) + 1
	}
	c := sep[0]
	n := 0
	for i := 0; i+len(sep) <= len(s); i++ {
		if s[i] == c && (len(sep) == 1 || s[i:i+len(sep)] == sep) {
			n++
			i += len(sep) - 1
		}
	}
	return n
}

// Index returns the index of the first instance of sep in s, or -1 if sep is not present in s.
func Index(s, sep string) int {
	n := len(sep)
	if n == 0 {
		return 0
	}
	c := sep[0]
	if n == 1 {
		// special case worth making fast
		for i := 0; i < len(s); i++ {
			if s[i] == c {
				return i
			}
		}
		return -1
	}
	// n > 1
	for i := 0; i+n <= len(s); i++ {
		if s[i] == c && s[i:i+n] == sep {
			return i
		}
	}
	return -1
}

// LastIndex returns the index of the last instance of sep in s, or -1 if sep is not present in s.
func LastIndex(s, sep string) int {
	n := len(sep)
	if n == 0 {
		return len(s)
	}
	c := sep[0]
	if n == 1 {
		// special case worth making fast
		for i := len(s) - 1; i >= 0; i-- {
			if s[i] == c {
				return i
			}
		}
		return -1
	}
	// n > 1
	for i := len(s) - n; i >= 0; i-- {
		if s[i] == c && s[i:i+n] == sep {
			return i
		}
	}
	return -1
}

// Generic split: splits after each instance of sep,
// including sepSave bytes of sep in the subarrays.
func genSplit(s, sep string, sepSave, n int) []string {
	if sep == "" {
		return explode(s, n)
	}
	if n <= 0 {
		n = Count(s, sep) + 1
	}
	c := sep[0]
	start := 0
	a := make([]string, n)
	na := 0
	for i := 0; i+len(sep) <= len(s) && na+1 < n; i++ {
		if s[i] == c && (len(sep) == 1 || s[i:i+len(sep)] == sep) {
			a[na] = s[start : i+sepSave]
			na++
			start = i + len(sep)
			i += len(sep) - 1
		}
	}
	a[na] = s[start:]
	return a[0 : na+1]
}

// Split splits the string s around each instance of sep, returning an array of substrings of s.
// If sep is empty, Split splits s after each UTF-8 sequence.
// If n > 0, Split splits s into at most n substrings; the last substring will be the unsplit remainder.
func Split(s, sep string, n int) []string { return genSplit(s, sep, 0, n) }

// SplitAfter splits the string s after each instance of sep, returning an array of substrings of s.
// If sep is empty, SplitAfter splits s after each UTF-8 sequence.
// If n > 0, SplitAfter splits s into at most n substrings; the last substring will be the unsplit remainder.
func SplitAfter(s, sep string, n int) []string {
	return genSplit(s, sep, len(sep), n)
}

// Fields splits the string s around each instance of one or more consecutive white space
// characters, returning an array of substrings of s or an empty list if s contains only white space.
func Fields(s string) []string {
	n := 0
	inField := false
	for _, rune := range s {
		wasInField := inField
		inField = !unicode.IsSpace(rune)
		if inField && !wasInField {
			n++
		}
	}

	a := make([]string, n)
	na := 0
	fieldStart := -1
	for i, rune := range s {
		if unicode.IsSpace(rune) {
			if fieldStart >= 0 {
				a[na] = s[fieldStart:i]
				na++
				fieldStart = -1
			}
		} else if fieldStart == -1 {
			fieldStart = i
		}
	}
	if fieldStart != -1 {
		a[na] = s[fieldStart:]
		na++
	}
	return a[0:na]
}

// Join concatenates the elements of a to create a single string.   The separator string
// sep is placed between elements in the resulting string.
func Join(a []string, sep string) string {
	if len(a) == 0 {
		return ""
	}
	if len(a) == 1 {
		return a[0]
	}
	n := len(sep) * (len(a) - 1)
	for i := 0; i < len(a); i++ {
		n += len(a[i])
	}

	b := make([]byte, n)
	bp := 0
	for i := 0; i < len(a); i++ {
		s := a[i]
		for j := 0; j < len(s); j++ {
			b[bp] = s[j]
			bp++
		}
		if i+1 < len(a) {
			s = sep
			for j := 0; j < len(s); j++ {
				b[bp] = s[j]
				bp++
			}
		}
	}
	return string(b)
}

// HasPrefix tests whether the string s begins with prefix.
func HasPrefix(s, prefix string) bool {
	return len(s) >= len(prefix) && s[0:len(prefix)] == prefix
}

// HasSuffix tests whether the string s ends with suffix.
func HasSuffix(s, suffix string) bool {
	return len(s) >= len(suffix) && s[len(s)-len(suffix):] == suffix
}

// Map returns a copy of the string s with all its characters modified
// according to the mapping function. If mapping returns a negative value, the character is
// dropped from the string with no replacement.
func Map(mapping func(rune int) int, s string) string {
	// In the worst case, the string can grow when mapped, making
	// things unpleasant.  But it's so rare we barge in assuming it's
	// fine.  It could also shrink but that falls out naturally.
	maxbytes := len(s) // length of b
	nbytes := 0        // number of bytes encoded in b
	b := make([]byte, maxbytes)
	for _, c := range s {
		rune := mapping(c)
		if rune >= 0 {
			wid := 1
			if rune >= utf8.RuneSelf {
				wid = utf8.RuneLen(rune)
			}
			if nbytes+wid > maxbytes {
				// Grow the buffer.
				maxbytes = maxbytes*2 + utf8.UTFMax
				nb := make([]byte, maxbytes)
				for i, c := range b[0:nbytes] {
					nb[i] = c
				}
				b = nb
			}
			nbytes += utf8.EncodeRune(rune, b[nbytes:maxbytes])
		}
	}
	return string(b[0:nbytes])
}

// Repeat returns a new string consisting of count copies of the string s.
func Repeat(s string, count int) string {
	b := make([]byte, len(s)*count)
	bp := 0
	for i := 0; i < count; i++ {
		for j := 0; j < len(s); j++ {
			b[bp] = s[j]
			bp++
		}
	}
	return string(b)
}

// ToUpper returns a copy of the string s with all Unicode letters mapped to their upper case.
func ToUpper(s string) string { return Map(unicode.ToUpper, s) }

// ToLower returns a copy of the string s with all Unicode letters mapped to their lower case.
func ToLower(s string) string { return Map(unicode.ToLower, s) }

// ToTitle returns a copy of the string s with all Unicode letters mapped to their title case.
func ToTitle(s string) string { return Map(unicode.ToTitle, s) }

// Trim returns a slice of the string s, with all leading and trailing white space
// removed, as defined by Unicode.
func TrimSpace(s string) string {
	start, end := 0, len(s)
	for start < end {
		wid := 1
		rune := int(s[start])
		if rune >= utf8.RuneSelf {
			rune, wid = utf8.DecodeRuneInString(s[start:end])
		}
		if !unicode.IsSpace(rune) {
			break
		}
		start += wid
	}
	for start < end {
		wid := 1
		rune := int(s[end-1])
		if rune >= utf8.RuneSelf {
			// Back up carefully looking for beginning of rune. Mustn't pass start.
			for wid = 2; start <= end-wid && !utf8.RuneStart(s[end-wid]); wid++ {
			}
			if start > end-wid { // invalid UTF-8 sequence; stop processing
				return s[start:end]
			}
			rune, wid = utf8.DecodeRuneInString(s[end-wid : end])
		}
		if !unicode.IsSpace(rune) {
			break
		}
		end -= wid
	}
	return s[start:end]
}

// Bytes returns a new slice containing the bytes in s.
func Bytes(s string) []byte {
	b := make([]byte, len(s))
	for i := 0; i < len(s); i++ {
		b[i] = s[i]
	}
	return b
}

// Runes returns a slice of runes (Unicode code points) equivalent to the string s.
func Runes(s string) []int {
	t := make([]int, utf8.RuneCountInString(s))
	i := 0
	for _, r := range s {
		t[i] = r
		i++
	}
	return t
}
<|endoftext|>


<commit_before>package imgbom

import (
	"github.com/anchore/imgbom/imgbom/cataloger"
	"github.com/anchore/imgbom/imgbom/distro"
	"github.com/anchore/imgbom/imgbom/logger"
	"github.com/anchore/imgbom/imgbom/pkg"
	"github.com/anchore/imgbom/imgbom/scope"
	"github.com/anchore/imgbom/internal/bus"
	"github.com/anchore/imgbom/internal/log"
	"github.com/anchore/stereoscope/pkg/image"
	"github.com/wagoodman/go-partybus"
)

func IdentifyDistro(s scope.Scope) *distro.Distro {
	return distro.Identify(s)
}

func GetScopeFromDir(d string, o scope.Option) (scope.Scope, error) {
	return scope.NewScopeFromDir(d, o)
}

func GetScopeFromImage(img *image.Image, o scope.Option) (scope.Scope, error) {
	return scope.NewScopeFromImage(img, o)
}

func Catalog(s scope.Scope) (*pkg.Catalog, error) {
	return cataloger.Catalog(s)
}

func SetLogger(logger logger.Logger) {
	log.Log = logger
}

func SetBus(b *partybus.Bus) {
	bus.SetPublisher(b)
}
<commit_msg>lib: implement a NewScope to produce scopes from inputs<commit_after>package imgbom

import (
	"fmt"

	"github.com/anchore/imgbom/imgbom/cataloger"
	"github.com/anchore/imgbom/imgbom/distro"
	"github.com/anchore/imgbom/imgbom/logger"
	"github.com/anchore/imgbom/imgbom/pkg"
	"github.com/anchore/imgbom/imgbom/scope"
	"github.com/anchore/imgbom/internal/bus"
	"github.com/anchore/imgbom/internal/log"
	"github.com/anchore/stereoscope"
	"github.com/anchore/stereoscope/pkg/image"
	"github.com/wagoodman/go-partybus"
)

func IdentifyDistro(s scope.Scope) *distro.Distro {
	return distro.Identify(s)
}

// NewScope produces a Scope based on userInput like dir:// or image:tag
func NewScope(userInput string, o scope.Option) (scope.Scope, func(), error) {
	protocol := NewProtocol(userInput)
	log.Debugf("protocol: %+v", protocol)

	switch protocol.Type {
	case DirProtocol:
		// populate the scope object for dir
		s, err := GetScopeFromDir(protocol.Value, o)
		if err != nil {
			return scope.Scope{}, func() {}, fmt.Errorf("could not populate scope from path (%s): %w", protocol.Value, err)
		}
		return s, func() {}, nil

	case ImageProtocol:
		log.Infof("Fetching image '%s'", userInput)
		img, err := stereoscope.GetImage(userInput)
		cleanup := func() {
			stereoscope.Cleanup()
		}

		if err != nil || img == nil {
			return scope.Scope{}, cleanup, fmt.Errorf("could not fetch image '%s': %w", userInput, err)
		}

		s, err := GetScopeFromImage(img, o)
		if err != nil {
			return scope.Scope{}, cleanup, fmt.Errorf("could not populate scope with image: %w", err)
		}
		return s, cleanup, nil

	default:
		return scope.Scope{}, func() {}, fmt.Errorf("unable to process input for scanning: '%s'", userInput)
	}
}

func GetScopeFromDir(d string, o scope.Option) (scope.Scope, error) {
	return scope.NewScopeFromDir(d, o)
}

func GetScopeFromImage(img *image.Image, o scope.Option) (scope.Scope, error) {
	return scope.NewScopeFromImage(img, o)
}

func Catalog(s scope.Scope) (*pkg.Catalog, error) {
	return cataloger.Catalog(s)
}

func SetLogger(logger logger.Logger) {
	log.Log = logger
}

func SetBus(b *partybus.Bus) {
	bus.SetPublisher(b)
}
<|endoftext|>


<commit_before>package controllers

import (
	"database/sql"
	// _ "github.com/go-sql-driver/mysql"
	"github.com/robfig/revel"
	"github.com/robfig/revel/modules/db/app"
	"math/rand"
	"runtime"
	"sort"
	"sync"
)

type MessageStruct struct {
	Message string `json:"message"`
}

type World struct {
	Id           uint16 `json:"id"`
	RandomNumber uint16 `json:"randomNumber"`
}

type Fortune struct {
	Id      uint16 `json:"id"`
	Message string `json:"message"`
}

const (
	WorldSelect        = "SELECT id,randomNumber FROM World where id=?"
	FortuneSelect      = "SELECT id,message FROM Fortune"
	WorldRowCount      = 10000
	MaxConnectionCount = 100
)

var (
	worldStatement   *sql.Stmt
	fortuneStatement *sql.Stmt
)

func init() {
	revel.OnAppStart(func() {
		var err error
		runtime.GOMAXPROCS(runtime.NumCPU())
		db.DbPlugin{}.OnAppStart()
		db.Db.SetMaxIdleConns(MaxConnectionCount)
		if worldStatement, err = db.Db.Prepare(WorldSelect); err != nil {
			revel.ERROR.Fatalln(err)
		}
		if fortuneStatement, err = db.Db.Prepare(FortuneSelect); err != nil {
			revel.ERROR.Fatalln(err)
		}
	})
}

type App struct {
	*revel.Controller
}

func (c App) Json() revel.Result {
	c.Response.ContentType = "application/json"
	return c.RenderJson(MessageStruct{"Hello, world"})
}

func (c App) Db(queries int) revel.Result {
	rowNum := rand.Intn(WorldRowCount) + 1
	if queries <= 1 {
		var w World
		worldStatement.QueryRow(rowNum).Scan(&w.Id, &w.RandomNumber)
		return c.RenderJson(w)
	}

	ww := make([]World, queries)
	var wg sync.WaitGroup
	wg.Add(queries)
	for i := 0; i < queries; i++ {
		go func(i int) {
			err := worldStatement.QueryRow(rowNum).Scan(&ww[i].Id, &ww[i].RandomNumber)
			if err != nil {
				revel.ERROR.Fatalf("Error scanning world row: %v", err)
			}
			wg.Done()
		}(i)
	}
	wg.Wait()
	return c.RenderJson(ww)
}

func (c App) Fortune() revel.Result {
	fortunes := make([]*Fortune, 0, 16)

	rows, err := fortuneStatement.Query()
	if err != nil {
		revel.ERROR.Fatalf("Error preparing statement: %v", err)
	}

	i := 0
	var fortune *Fortune
	for rows.Next() {
		fortune = new(Fortune)
		if err = rows.Scan(&fortune.Id, &fortune.Message); err != nil {
			revel.ERROR.Fatalf("Error scanning fortune row: %v", err)
		}
		fortunes = append(fortunes, fortune)
		i++
	}
	fortunes = append(fortunes, &Fortune{Message: "Additional fortune added at request time."})

	sort.Sort(ByMessage{fortunes})
	return c.Render(fortunes)
}

type Fortunes []*Fortune

func (s Fortunes) Len() int      { return len(s) }
func (s Fortunes) Swap(i, j int) { s[i], s[j] = s[j], s[i] }

type ByMessage struct{ Fortunes }

func (s ByMessage) Less(i, j int) bool { return s.Fortunes[i].Message < s.Fortunes[j].Message }
<commit_msg>Fix: pick different rows for concurrent reqs<commit_after>package controllers

import (
	"database/sql"
	// _ "github.com/go-sql-driver/mysql"
	"github.com/robfig/revel"
	"github.com/robfig/revel/modules/db/app"
	"math/rand"
	"runtime"
	"sort"
	"sync"
)

type MessageStruct struct {
	Message string `json:"message"`
}

type World struct {
	Id           uint16 `json:"id"`
	RandomNumber uint16 `json:"randomNumber"`
}

type Fortune struct {
	Id      uint16 `json:"id"`
	Message string `json:"message"`
}

const (
	WorldSelect        = "SELECT id,randomNumber FROM World where id=?"
	FortuneSelect      = "SELECT id,message FROM Fortune"
	WorldRowCount      = 10000
	MaxConnectionCount = 100
)

var (
	worldStatement   *sql.Stmt
	fortuneStatement *sql.Stmt
)

func init() {
	revel.OnAppStart(func() {
		var err error
		runtime.GOMAXPROCS(runtime.NumCPU())
		db.DbPlugin{}.OnAppStart()
		db.Db.SetMaxIdleConns(MaxConnectionCount)
		if worldStatement, err = db.Db.Prepare(WorldSelect); err != nil {
			revel.ERROR.Fatalln(err)
		}
		if fortuneStatement, err = db.Db.Prepare(FortuneSelect); err != nil {
			revel.ERROR.Fatalln(err)
		}
	})
}

type App struct {
	*revel.Controller
}

func (c App) Json() revel.Result {
	c.Response.ContentType = "application/json"
	return c.RenderJson(MessageStruct{"Hello, world"})
}

func (c App) Db(queries int) revel.Result {
	if queries <= 1 {
		rowNum := rand.Intn(WorldRowCount) + 1
		var w World
		worldStatement.QueryRow(rowNum).Scan(&w.Id, &w.RandomNumber)
		return c.RenderJson(w)
	}

	ww := make([]World, queries)
	var wg sync.WaitGroup
	wg.Add(queries)
	for i := 0; i < queries; i++ {
		go func(i int) {
			rowNum := rand.Intn(WorldRowCount) + 1
			err := worldStatement.QueryRow(rowNum).Scan(&ww[i].Id, &ww[i].RandomNumber)
			if err != nil {
				revel.ERROR.Fatalf("Error scanning world row: %v", err)
			}
			wg.Done()
		}(i)
	}
	wg.Wait()
	return c.RenderJson(ww)
}

func (c App) Fortune() revel.Result {
	fortunes := make([]*Fortune, 0, 16)

	rows, err := fortuneStatement.Query()
	if err != nil {
		revel.ERROR.Fatalf("Error preparing statement: %v", err)
	}

	i := 0
	var fortune *Fortune
	for rows.Next() {
		fortune = new(Fortune)
		if err = rows.Scan(&fortune.Id, &fortune.Message); err != nil {
			revel.ERROR.Fatalf("Error scanning fortune row: %v", err)
		}
		fortunes = append(fortunes, fortune)
		i++
	}
	fortunes = append(fortunes, &Fortune{Message: "Additional fortune added at request time."})

	sort.Sort(ByMessage{fortunes})
	return c.Render(fortunes)
}

type Fortunes []*Fortune

func (s Fortunes) Len() int      { return len(s) }
func (s Fortunes) Swap(i, j int) { s[i], s[j] = s[j], s[i] }

type ByMessage struct{ Fortunes }

func (s ByMessage) Less(i, j int) bool { return s.Fortunes[i].Message < s.Fortunes[j].Message }
<|endoftext|>


<commit_before>package v7

import (
	"strings"

	"code.cloudfoundry.org/cli/util/ui"
)

type IsolationSegmentsCommand struct {
	BaseCommand
	usage           interface{} `usage:"CF_NAME isolation-segments"`
	relatedCommands interface{} `related_commands:"enable-org-isolation, create-isolation-segment"`
}

func (cmd IsolationSegmentsCommand) Execute(args []string) error {
	err := cmd.SharedActor.CheckTarget(false, false)
	if err != nil {
		return err
	}

	user, err := cmd.Config.CurrentUser()
	if err != nil {
		return err
	}

	cmd.UI.DisplayTextWithFlavor("Getting isolation segments as {{.CurrentUser}}...", map[string]interface{}{
		"CurrentUser": user.Name,
	})

	summaries, warnings, err := cmd.Actor.GetIsolationSegmentSummaries()
	cmd.UI.DisplayWarnings(warnings)
	if err != nil {
		return err
	}

	table := [][]string{
		{
			cmd.UI.TranslateText("name"),
			cmd.UI.TranslateText("orgs"),
		},
	}

	for _, summary := range summaries {
		table = append(
			table,
			[]string{
				summary.Name,
				strings.Join(summary.EntitledOrgs, ", "),
			},
		)
	}

	cmd.UI.DisplayTableWithHeader("", table, ui.DefaultTableSpacePadding)
	return nil
}
<commit_msg>**V7**: add whitespace to list isolation segments<commit_after>package v7

import (
	"strings"

	"code.cloudfoundry.org/cli/util/ui"
)

type IsolationSegmentsCommand struct {
	BaseCommand
	usage           interface{} `usage:"CF_NAME isolation-segments"`
	relatedCommands interface{} `related_commands:"enable-org-isolation, create-isolation-segment"`
}

func (cmd IsolationSegmentsCommand) Execute(args []string) error {
	err := cmd.SharedActor.CheckTarget(false, false)
	if err != nil {
		return err
	}

	user, err := cmd.Config.CurrentUser()
	if err != nil {
		return err
	}

	cmd.UI.DisplayTextWithFlavor("Getting isolation segments as {{.CurrentUser}}...", map[string]interface{}{
		"CurrentUser": user.Name,
	})

	cmd.UI.DisplayNewline()

	summaries, warnings, err := cmd.Actor.GetIsolationSegmentSummaries()
	cmd.UI.DisplayWarnings(warnings)
	if err != nil {
		return err
	}

	table := [][]string{
		{
			cmd.UI.TranslateText("name"),
			cmd.UI.TranslateText("orgs"),
		},
	}

	for _, summary := range summaries {
		table = append(
			table,
			[]string{
				summary.Name,
				strings.Join(summary.EntitledOrgs, ", "),
			},
		)
	}

	cmd.UI.DisplayTableWithHeader("", table, ui.DefaultTableSpacePadding)
	return nil
}
<|endoftext|>


<commit_before>package fritz

import (
	"log"
	"testing"

	"github.com/stretchr/testify/assert"
)

// TestAPIGetSwitchList unit test.
func TestAPIGetSwitchList(t *testing.T) {
	ts, fritzClient := serverAndClient("testdata/examplechallenge_test.xml", "testdata/examplechallenge_sid_test.xml")
	defer ts.Close()
	fritzClient.Login()
	fritz := UsingClient(fritzClient)
	_, err := fritz.GetSwitchList()
	assert.NoError(t, err)
}

// TestAPIGetSwitchListErrorServerDown unit test.
func TestAPIGetSwitchListErrorServerDown(t *testing.T) {
	ts, fritzClient := serverAndClient("testdata/examplechallenge_test.xml", "testdata/examplechallenge_sid_test.xml")
	defer ts.Close()
	fritzClient.Login()
	fritz := UsingClient(fritzClient)
	ts.Close()
	_, err := fritz.GetSwitchList()
	assert.Error(t, err)
}

// TestGetWithAin unit test.
func TestGetWithAin(t *testing.T) {
	ts, fritzClient := serverAndClient("testdata/examplechallenge_test.xml", "testdata/examplechallenge_sid_test.xml")
	defer ts.Close()
	fritzClient.Login()
	fritz := UsingClient(fritzClient).(*fritzImpl)
	_, err := fritz.getWithAinAndParam("ain", "cmd", "x=y")
	assert.NoError(t, err)
}

// TestGetDeviceList unit test.
func TestGetDeviceList(t *testing.T) {
	ts, fritzClient := serverAndClient("testdata/examplechallenge_sid_test.xml",
		"testdata/examplechallenge_sid_test.xml", "testdata/devicelist_test.xml")
	defer ts.Close()
	fritzClient.Login()
	fritz := UsingClient(fritzClient)
	devList, err := fritz.ListDevices()
	log.Println(*devList)
	assert.NoError(t, err)
	assert.NotNil(t, devList)
	assert.NotEmpty(t, devList.Devices)
	assert.NotEmpty(t, devList.Devices[0].ID)
	assert.NotEmpty(t, devList.Devices[0].Identifier)
	assert.NotEmpty(t, devList.Devices[0].Functionbitmask)
	assert.NotEmpty(t, devList.Devices[0].Fwversion)
	assert.NotEmpty(t, devList.Devices[0].Manufacturer)
	assert.Equal(t, devList.Devices[0].Present, 1)
	assert.NotEmpty(t, devList.Devices[0].Name)

}

// TestAPIGetDeviceListErrorServerDown unit test.
func TestAPIGetDeviceListErrorServerDown(t *testing.T) {
	ts, fritzClient := serverAndClient("testdata/examplechallenge_test.xml", "testdata/examplechallenge_sid_test.xml")
	defer ts.Close()
	fritzClient.Login()
	fritz := UsingClient(fritzClient)
	ts.Close()
	_, err := fritz.ListDevices()
	assert.Error(t, err)
}

// TestAPIGetSwitchDeviceOn unit test.
func TestAPIGetSwitchDeviceOn(t *testing.T) {
	ts, fritzClient := serverAndClient("testdata/examplechallenge_test.xml", "testdata/examplechallenge_sid_test.xml", "testdata/devicelist_test.xml", "testdata/answer_switch_on_test")
	defer ts.Close()
	fritzClient.Login()
	fritz := UsingClient(fritzClient)
	resp, err := fritz.Switch("DER device", "on")
	assert.NoError(t, err)
	assert.NotEmpty(t, resp)
}

// TestAPIGetSwitchDeviceOff unit test.
func TestAPIGetSwitchDeviceOff(t *testing.T) {
	ts, fritzClient := serverAndClient("testdata/examplechallenge_test.xml", "testdata/examplechallenge_sid_test.xml", "testdata/devicelist_test.xml", "testdata/answer_switch_on_test")
	defer ts.Close()
	fritzClient.Login()
	fritz := UsingClient(fritzClient)
	resp, err := fritz.Switch("DER device", "off")
	assert.NoError(t, err)
	assert.NotEmpty(t, resp)
}

// TestAPIGetSwitchDeviceErrorServerDownAtListingStage unit test.
func TestAPIGetSwitchDeviceErrorServerDownAtListingStage(t *testing.T) {
	ts, fritzClient := serverAndClient("testdata/examplechallenge_test.xml", "testdata/examplechallenge_sid_test.xml", "testdata/devicelist_test.xml", "testdata/answer_switch_on_test")
	defer ts.Close()
	fritzClient.Login()
	ts.Close()
	fritz := UsingClient(fritzClient)
	_, err := fritz.Switch("DER device", "off")
	assert.Error(t, err)
}

// TestAPIGetSwitchDeviceErrorUnkownDevice unit test.
func TestAPIGetSwitchDeviceErrorUnkownDevice(t *testing.T) {
	ts, fritzClient := serverAndClient("testdata/examplechallenge_test.xml", "testdata/examplechallenge_sid_test.xml", "testdata/devicelist_empty_test.xml")
	defer ts.Close()
	fritzClient.Login()
	fritz := UsingClient(fritzClient)
	_, err := fritz.Switch("DER device", "off")
	assert.Error(t, err)
}

// TestAPIGetSwitchByAinWithError unit test.
func TestAPIGetSwitchByAinWithError(t *testing.T) {
	ts, fritzClient := serverAndClient("testdata/examplechallenge_test.xml", "testdata/examplechallenge_sid_test.xml", "testdata/devicelist_empty_test.xml")
	defer ts.Close()
	fritzClient.Login()
	fritz := UsingClient(fritzClient).(*fritzImpl)
	ts.Close()
	_, err := fritz.switchForAin("123344", "off")
	assert.Error(t, err)
}

// TestAPIToggleDevice unit test.
func TestAPIToggleDevice(t *testing.T) {
	ts, fritzClient := serverAndClient("testdata/examplechallenge_test.xml", "testdata/examplechallenge_sid_test.xml", "testdata/devicelist_test.xml", "testdata/answer_switch_on_test")
	defer ts.Close()
	fritzClient.Login()
	fritz := UsingClient(fritzClient)
	resp, err := fritz.Toggle("DER device")
	assert.NoError(t, err)
	assert.NotEmpty(t, resp)
}

// TestAPIToggleDeviceErrorServerDownAtListingStage unit test.
func TestAPIToggleDeviceErrorServerDownAtListingStage(t *testing.T) {
	ts, fritzClient := serverAndClient("testdata/examplechallenge_test.xml", "testdata/examplechallenge_sid_test.xml", "testdata/devicelist_test.xml", "testdata/answer_switch_on_test")
	defer ts.Close()
	fritzClient.Login()
	ts.Close()
	fritz := UsingClient(fritzClient)
	_, err := fritz.Toggle("DER device")
	assert.Error(t, err)
}

// TestAPIToggleDeviceErrorServerDownAtToggleStage unit test.
func TestAPIToggleDeviceErrorServerDownAtToggleStage(t *testing.T) {
	ts, fritzClient := serverAndClient("testdata/examplechallenge_test.xml", "testdata/examplechallenge_sid_test.xml", "testdata/devicelist_test.xml", "testdata/answer_switch_on_test")
	defer ts.Close()
	fritzClient.Login()
	fritz := UsingClient(fritzClient).(*fritzImpl)
	ts.Close()
	_, err := fritz.toggleForAin("DER device")
	assert.Error(t, err)
}

// TestAPISetHkr unit test.
func TestAPISetHkr(t *testing.T) {
	ts, fritzClient := serverAndClient("testdata/examplechallenge_test.xml", "testdata/examplechallenge_sid_test.xml", "testdata/devicelist_test.xml", "testdata/answer_switch_on_test")
	defer ts.Close()
	fritzClient.Login()
	fritz := UsingClient(fritzClient).(*fritzImpl)
	_, err := fritz.Temperature("DER device", 12.5)
	assert.NoError(t, err)
}

// TestAPISetHkrDevNotFound unit test.
func TestAPISetHkrDevNotFound(t *testing.T) {
	ts, fritzClient := serverAndClient("testdata/examplechallenge_test.xml", "testdata/examplechallenge_sid_test.xml", "testdata/devicelist_test.xml", "testdata/answer_switch_on_test")
	defer ts.Close()
	fritzClient.Login()
	fritz := UsingClient(fritzClient).(*fritzImpl)
	_, err := fritz.Temperature("DOES-NOT-EXIST", 12.5)
	assert.Error(t, err)
}

// TestAPISetHkrErrorServerDownAtCommandStage unit test.
func TestAPISetHkrErrorServerDownAtCommandStage(t *testing.T) {
	ts, fritzClient := serverAndClient("testdata/examplechallenge_test.xml", "testdata/examplechallenge_sid_test.xml", "testdata/devicelist_test.xml", "testdata/answer_switch_on_test")
	defer ts.Close()
	fritzClient.Login()
	fritz := UsingClient(fritzClient).(*fritzImpl)
	ts.Close()
	_, err := fritz.temperatureForAin("12345", 12.5)
	assert.Error(t, err)
}

func TestRd(t *testing.T) {
	assert.Equal(t, int64(1), round(0.5))
	assert.Equal(t, int64(0), round(0.4))
	assert.Equal(t, int64(0), round(0.1))
	assert.Equal(t, int64(0), round(-0.1))
	assert.Equal(t, int64(0), round(-0.499))
	assert.Equal(t, int64(156), round(156))
}
<commit_msg>add comment to test<commit_after>package fritz

import (
	"log"
	"testing"

	"github.com/stretchr/testify/assert"
)

// TestAPIGetSwitchList unit test.
func TestAPIGetSwitchList(t *testing.T) {
	ts, fritzClient := serverAndClient("testdata/examplechallenge_test.xml", "testdata/examplechallenge_sid_test.xml")
	defer ts.Close()
	fritzClient.Login()
	fritz := UsingClient(fritzClient)
	_, err := fritz.GetSwitchList()
	assert.NoError(t, err)
}

// TestAPIGetSwitchListErrorServerDown unit test.
func TestAPIGetSwitchListErrorServerDown(t *testing.T) {
	ts, fritzClient := serverAndClient("testdata/examplechallenge_test.xml", "testdata/examplechallenge_sid_test.xml")
	defer ts.Close()
	fritzClient.Login()
	fritz := UsingClient(fritzClient)
	ts.Close()
	_, err := fritz.GetSwitchList()
	assert.Error(t, err)
}

// TestGetWithAin unit test.
func TestGetWithAin(t *testing.T) {
	ts, fritzClient := serverAndClient("testdata/examplechallenge_test.xml", "testdata/examplechallenge_sid_test.xml")
	defer ts.Close()
	fritzClient.Login()
	fritz := UsingClient(fritzClient).(*fritzImpl)
	_, err := fritz.getWithAinAndParam("ain", "cmd", "x=y")
	assert.NoError(t, err)
}

// TestGetDeviceList unit test.
func TestGetDeviceList(t *testing.T) {
	ts, fritzClient := serverAndClient("testdata/examplechallenge_sid_test.xml",
		"testdata/examplechallenge_sid_test.xml", "testdata/devicelist_test.xml")
	defer ts.Close()
	fritzClient.Login()
	fritz := UsingClient(fritzClient)
	devList, err := fritz.ListDevices()
	log.Println(*devList)
	assert.NoError(t, err)
	assert.NotNil(t, devList)
	assert.NotEmpty(t, devList.Devices)
	assert.NotEmpty(t, devList.Devices[0].ID)
	assert.NotEmpty(t, devList.Devices[0].Identifier)
	assert.NotEmpty(t, devList.Devices[0].Functionbitmask)
	assert.NotEmpty(t, devList.Devices[0].Fwversion)
	assert.NotEmpty(t, devList.Devices[0].Manufacturer)
	assert.Equal(t, devList.Devices[0].Present, 1)
	assert.NotEmpty(t, devList.Devices[0].Name)

}

// TestAPIGetDeviceListErrorServerDown unit test.
func TestAPIGetDeviceListErrorServerDown(t *testing.T) {
	ts, fritzClient := serverAndClient("testdata/examplechallenge_test.xml", "testdata/examplechallenge_sid_test.xml")
	defer ts.Close()
	fritzClient.Login()
	fritz := UsingClient(fritzClient)
	ts.Close()
	_, err := fritz.ListDevices()
	assert.Error(t, err)
}

// TestAPIGetSwitchDeviceOn unit test.
func TestAPIGetSwitchDeviceOn(t *testing.T) {
	ts, fritzClient := serverAndClient("testdata/examplechallenge_test.xml", "testdata/examplechallenge_sid_test.xml", "testdata/devicelist_test.xml", "testdata/answer_switch_on_test")
	defer ts.Close()
	fritzClient.Login()
	fritz := UsingClient(fritzClient)
	resp, err := fritz.Switch("DER device", "on")
	assert.NoError(t, err)
	assert.NotEmpty(t, resp)
}

// TestAPIGetSwitchDeviceOff unit test.
func TestAPIGetSwitchDeviceOff(t *testing.T) {
	ts, fritzClient := serverAndClient("testdata/examplechallenge_test.xml", "testdata/examplechallenge_sid_test.xml", "testdata/devicelist_test.xml", "testdata/answer_switch_on_test")
	defer ts.Close()
	fritzClient.Login()
	fritz := UsingClient(fritzClient)
	resp, err := fritz.Switch("DER device", "off")
	assert.NoError(t, err)
	assert.NotEmpty(t, resp)
}

// TestAPIGetSwitchDeviceErrorServerDownAtListingStage unit test.
func TestAPIGetSwitchDeviceErrorServerDownAtListingStage(t *testing.T) {
	ts, fritzClient := serverAndClient("testdata/examplechallenge_test.xml", "testdata/examplechallenge_sid_test.xml", "testdata/devicelist_test.xml", "testdata/answer_switch_on_test")
	defer ts.Close()
	fritzClient.Login()
	ts.Close()
	fritz := UsingClient(fritzClient)
	_, err := fritz.Switch("DER device", "off")
	assert.Error(t, err)
}

// TestAPIGetSwitchDeviceErrorUnkownDevice unit test.
func TestAPIGetSwitchDeviceErrorUnkownDevice(t *testing.T) {
	ts, fritzClient := serverAndClient("testdata/examplechallenge_test.xml", "testdata/examplechallenge_sid_test.xml", "testdata/devicelist_empty_test.xml")
	defer ts.Close()
	fritzClient.Login()
	fritz := UsingClient(fritzClient)
	_, err := fritz.Switch("DER device", "off")
	assert.Error(t, err)
}

// TestAPIGetSwitchByAinWithError unit test.
func TestAPIGetSwitchByAinWithError(t *testing.T) {
	ts, fritzClient := serverAndClient("testdata/examplechallenge_test.xml", "testdata/examplechallenge_sid_test.xml", "testdata/devicelist_empty_test.xml")
	defer ts.Close()
	fritzClient.Login()
	fritz := UsingClient(fritzClient).(*fritzImpl)
	ts.Close()
	_, err := fritz.switchForAin("123344", "off")
	assert.Error(t, err)
}

// TestAPIToggleDevice unit test.
func TestAPIToggleDevice(t *testing.T) {
	ts, fritzClient := serverAndClient("testdata/examplechallenge_test.xml", "testdata/examplechallenge_sid_test.xml", "testdata/devicelist_test.xml", "testdata/answer_switch_on_test")
	defer ts.Close()
	fritzClient.Login()
	fritz := UsingClient(fritzClient)
	resp, err := fritz.Toggle("DER device")
	assert.NoError(t, err)
	assert.NotEmpty(t, resp)
}

// TestAPIToggleDeviceErrorServerDownAtListingStage unit test.
func TestAPIToggleDeviceErrorServerDownAtListingStage(t *testing.T) {
	ts, fritzClient := serverAndClient("testdata/examplechallenge_test.xml", "testdata/examplechallenge_sid_test.xml", "testdata/devicelist_test.xml", "testdata/answer_switch_on_test")
	defer ts.Close()
	fritzClient.Login()
	ts.Close()
	fritz := UsingClient(fritzClient)
	_, err := fritz.Toggle("DER device")
	assert.Error(t, err)
}

// TestAPIToggleDeviceErrorServerDownAtToggleStage unit test.
func TestAPIToggleDeviceErrorServerDownAtToggleStage(t *testing.T) {
	ts, fritzClient := serverAndClient("testdata/examplechallenge_test.xml", "testdata/examplechallenge_sid_test.xml", "testdata/devicelist_test.xml", "testdata/answer_switch_on_test")
	defer ts.Close()
	fritzClient.Login()
	fritz := UsingClient(fritzClient).(*fritzImpl)
	ts.Close()
	_, err := fritz.toggleForAin("DER device")
	assert.Error(t, err)
}

// TestAPISetHkr unit test.
func TestAPISetHkr(t *testing.T) {
	ts, fritzClient := serverAndClient("testdata/examplechallenge_test.xml", "testdata/examplechallenge_sid_test.xml", "testdata/devicelist_test.xml", "testdata/answer_switch_on_test")
	defer ts.Close()
	fritzClient.Login()
	fritz := UsingClient(fritzClient).(*fritzImpl)
	_, err := fritz.Temperature("DER device", 12.5)
	assert.NoError(t, err)
}

// TestAPISetHkrDevNotFound unit test.
func TestAPISetHkrDevNotFound(t *testing.T) {
	ts, fritzClient := serverAndClient("testdata/examplechallenge_test.xml", "testdata/examplechallenge_sid_test.xml", "testdata/devicelist_test.xml", "testdata/answer_switch_on_test")
	defer ts.Close()
	fritzClient.Login()
	fritz := UsingClient(fritzClient).(*fritzImpl)
	_, err := fritz.Temperature("DOES-NOT-EXIST", 12.5)
	assert.Error(t, err)
}

// TestAPISetHkrErrorServerDownAtCommandStage unit test.
func TestAPISetHkrErrorServerDownAtCommandStage(t *testing.T) {
	ts, fritzClient := serverAndClient("testdata/examplechallenge_test.xml", "testdata/examplechallenge_sid_test.xml", "testdata/devicelist_test.xml", "testdata/answer_switch_on_test")
	defer ts.Close()
	fritzClient.Login()
	fritz := UsingClient(fritzClient).(*fritzImpl)
	ts.Close()
	_, err := fritz.temperatureForAin("12345", 12.5)
	assert.Error(t, err)
}

// TestRounding tests rounding.
func TestRounding(t *testing.T) {
	assert.Equal(t, int64(1), round(0.5))
	assert.Equal(t, int64(0), round(0.4))
	assert.Equal(t, int64(0), round(0.1))
	assert.Equal(t, int64(0), round(-0.1))
	assert.Equal(t, int64(0), round(-0.499))
	assert.Equal(t, int64(156), round(156))
}
<|endoftext|>