<commit_before>package cloudwatch

import (
	"testing"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/cloudwatch"
	"github.com/grafana/grafana/pkg/components/null"
	"github.com/grafana/grafana/pkg/tsdb"
	. "github.com/smartystreets/goconvey/convey"
)

func TestCloudWatchGetMetricData(t *testing.T) {
	Convey("CloudWatchGetMetricData", t, func() {

		Convey("can parse cloudwatch GetMetricData query", func() {
			queries := map[string]*CloudWatchQuery{
				"id1": {
					RefId:      "A",
					Region:     "us-east-1",
					Namespace:  "AWS/EC2",
					MetricName: "CPUUtilization",
					Dimensions: []*cloudwatch.Dimension{
						{
							Name:  aws.String("InstanceId"),
							Value: aws.String("i-12345678"),
						},
					},
					Statistics: []*string{aws.String("Average")},
					Period:     300,
					Id:         "id1",
					Expression: "",
					ReturnData: true,
				},
				"id2": {
					RefId:      "B",
					Region:     "us-east-1",
					Statistics: []*string{aws.String("Average")},
					Id:         "id2",
					Expression: "id1 * 2",
					ReturnData: true,
				},
			}
			queryContext := &tsdb.TsdbQuery{
				TimeRange: tsdb.NewFakeTimeRange("5m", "now", time.Now()),
			}
			res, err := parseGetMetricDataQuery(queries, queryContext)
			So(err, ShouldBeNil)
			So(*res.MetricDataQueries[0].MetricStat.Metric.Namespace, ShouldEqual, "AWS/EC2")
			So(*res.MetricDataQueries[0].MetricStat.Metric.MetricName, ShouldEqual, "CPUUtilization")
			So(*res.MetricDataQueries[0].MetricStat.Metric.Dimensions[0].Name, ShouldEqual, "InstanceId")
			So(*res.MetricDataQueries[0].MetricStat.Metric.Dimensions[0].Value, ShouldEqual, "i-12345678")
			So(*res.MetricDataQueries[0].MetricStat.Period, ShouldEqual, 300)
			So(*res.MetricDataQueries[0].MetricStat.Stat, ShouldEqual, "Average")
			So(*res.MetricDataQueries[0].Id, ShouldEqual, "id1")
			So(*res.MetricDataQueries[0].ReturnData, ShouldEqual, true)
			So(*res.MetricDataQueries[1].Id, ShouldEqual, "id2")
			So(*res.MetricDataQueries[1].Expression, ShouldEqual, "id1 * 2")
			So(*res.MetricDataQueries[1].ReturnData, ShouldEqual, true)
		})

		Convey("can parse cloudwatch response", func() {
			timestamp := time.Unix(0, 0)
			resp := map[string]*cloudwatch.MetricDataResult{
				"label": {
					Id:    aws.String("id1"),
					Label: aws.String("label"),
					Timestamps: []*time.Time{
						aws.Time(timestamp),
						aws.Time(timestamp.Add(60 * time.Second)),
						aws.Time(timestamp.Add(180 * time.Second)),
					},
					Values: []*float64{
						aws.Float64(10),
						aws.Float64(20),
						aws.Float64(30),
					},
					StatusCode: aws.String("Complete"),
				},
			}
			query := &CloudWatchQuery{
				RefId:      "refId1",
				Region:     "us-east-1",
				Namespace:  "AWS/ApplicationELB",
				MetricName: "TargetResponseTime",
				Dimensions: []*cloudwatch.Dimension{
					{
						Name:  aws.String("LoadBalancer"),
						Value: aws.String("lb"),
					},
					{
						Name:  aws.String("TargetGroup"),
						Value: aws.String("tg"),
					},
				},
				Statistics: []*string{aws.String("Average")},
				Period:     60,
				Alias:      "{{namespace}}_{{metric}}_{{stat}}",
			}
			queryRes, err := parseGetMetricDataResponse(resp, query)
			So(err, ShouldBeNil)
			So(queryRes.RefId, ShouldEqual, "refId1")
			So(queryRes.Series[0].Name, ShouldEqual, "AWS/ApplicationELB_TargetResponseTime_Average")
			So(queryRes.Series[0].Tags["LoadBalancer"], ShouldEqual, "lb")
			So(queryRes.Series[0].Tags["TargetGroup"], ShouldEqual, "tg")
			So(queryRes.Series[0].Points[0][0].String(), ShouldEqual, null.FloatFrom(10.0).String())
			So(queryRes.Series[0].Points[1][0].String(), ShouldEqual, null.FloatFrom(20.0).String())
			So(queryRes.Series[0].Points[2][0].String(), ShouldEqual, null.FloatFromPtr(nil).String())
			So(queryRes.Series[0].Points[3][0].String(), ShouldEqual, null.FloatFrom(30.0).String())
		})
	})
}
<commit_msg>Cloudwatch: fix for flaky tests (#16649)<commit_after>package cloudwatch

import (
	"testing"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/cloudwatch"
	"github.com/grafana/grafana/pkg/components/null"
	"github.com/grafana/grafana/pkg/tsdb"
	. "github.com/smartystreets/goconvey/convey"
)

func TestCloudWatchGetMetricData(t *testing.T) {
	Convey("CloudWatchGetMetricData", t, func() {

		Convey("can parse cloudwatch GetMetricData query", func() {
			queries := map[string]*CloudWatchQuery{
				"id1": {
					RefId:      "A",
					Region:     "us-east-1",
					Namespace:  "AWS/EC2",
					MetricName: "CPUUtilization",
					Dimensions: []*cloudwatch.Dimension{
						{
							Name:  aws.String("InstanceId"),
							Value: aws.String("i-12345678"),
						},
					},
					Statistics: []*string{aws.String("Average")},
					Period:     300,
					Id:         "id1",
					Expression: "",
					ReturnData: true,
				},
				"id2": {
					RefId:      "B",
					Region:     "us-east-1",
					Statistics: []*string{aws.String("Average")},
					Id:         "id2",
					Expression: "id1 * 2",
					ReturnData: true,
				},
			}

			queryContext := &tsdb.TsdbQuery{
				TimeRange: tsdb.NewFakeTimeRange("5m", "now", time.Now()),
			}
			res, err := parseGetMetricDataQuery(queries, queryContext)
			So(err, ShouldBeNil)

			for _, v := range res.MetricDataQueries {
				if *v.Id == "id1" {
					So(*v.MetricStat.Metric.Namespace, ShouldEqual, "AWS/EC2")
					So(*v.MetricStat.Metric.MetricName, ShouldEqual, "CPUUtilization")
					So(*v.MetricStat.Metric.Dimensions[0].Name, ShouldEqual, "InstanceId")
					So(*v.MetricStat.Metric.Dimensions[0].Value, ShouldEqual, "i-12345678")
					So(*v.MetricStat.Period, ShouldEqual, 300)
					So(*v.MetricStat.Stat, ShouldEqual, "Average")
					So(*v.Id, ShouldEqual, "id1")
					So(*v.ReturnData, ShouldEqual, true)
				} else {
					So(*v.Id, ShouldEqual, "id2")
					So(*v.Expression, ShouldEqual, "id1 * 2")
					So(*v.ReturnData, ShouldEqual, true)
				}
			}
		})

		Convey("can parse cloudwatch response", func() {
			timestamp := time.Unix(0, 0)
			resp := map[string]*cloudwatch.MetricDataResult{
				"label": {
					Id:    aws.String("id1"),
					Label: aws.String("label"),
					Timestamps: []*time.Time{
						aws.Time(timestamp),
						aws.Time(timestamp.Add(60 * time.Second)),
						aws.Time(timestamp.Add(180 * time.Second)),
					},
					Values: []*float64{
						aws.Float64(10),
						aws.Float64(20),
						aws.Float64(30),
					},
					StatusCode: aws.String("Complete"),
				},
			}
			query := &CloudWatchQuery{
				RefId:      "refId1",
				Region:     "us-east-1",
				Namespace:  "AWS/ApplicationELB",
				MetricName: "TargetResponseTime",
				Dimensions: []*cloudwatch.Dimension{
					{
						Name:  aws.String("LoadBalancer"),
						Value: aws.String("lb"),
					},
					{
						Name:  aws.String("TargetGroup"),
						Value: aws.String("tg"),
					},
				},
				Statistics: []*string{aws.String("Average")},
				Period:     60,
				Alias:      "{{namespace}}_{{metric}}_{{stat}}",
			}
			queryRes, err := parseGetMetricDataResponse(resp, query)
			So(err, ShouldBeNil)
			So(queryRes.RefId, ShouldEqual, "refId1")
			So(queryRes.Series[0].Name, ShouldEqual, "AWS/ApplicationELB_TargetResponseTime_Average")
			So(queryRes.Series[0].Tags["LoadBalancer"], ShouldEqual, "lb")
			So(queryRes.Series[0].Tags["TargetGroup"], ShouldEqual, "tg")
			So(queryRes.Series[0].Points[0][0].String(), ShouldEqual, null.FloatFrom(10.0).String())
			So(queryRes.Series[0].Points[1][0].String(), ShouldEqual, null.FloatFrom(20.0).String())
			So(queryRes.Series[0].Points[2][0].String(), ShouldEqual, null.FloatFromPtr(nil).String())
			So(queryRes.Series[0].Points[3][0].String(), ShouldEqual, null.FloatFrom(30.0).String())
		})
	})
}
<|endoftext|>


<commit_before>package opstocat

import (
	"fmt"
	"github.com/peterbourgon/g2s"
	"github.com/technoweenie/grohl"
	"log/syslog"
	"net/url"
	"os"
	"runtime"
	"time"
)

func SetupLogger(config ConfigWrapper) {
	innerconfig := config.OpstocatConfiguration()
	logch := make(chan grohl.Data, 100)
	chlogger, _ := grohl.NewChannelLogger(logch)
	grohl.SetLogger(chlogger)

	if len(innerconfig.StatsDAddress) > 0 {
		if innerconfig.StatsDAddress == "noop" {
			grohl.CurrentStatter = &NoOpStatter{}
		} else {
			statter, err := g2s.Dial("udp", innerconfig.StatsDAddress)
			if err != nil {
				grohl.Report(err, grohl.Data{"statsd_address": innerconfig.StatsDAddress})
				grohl.CurrentStatter = &NoOpStatter{}
			} else {
				grohl.CurrentStatter = statter
			}
		}
	}

	grohl.CurrentStatter = PrefixedStatter(innerconfig.App, grohl.CurrentStatter)

	if len(innerconfig.HaystackEndpoint) > 0 {
		reporter, err := NewHaystackReporter(innerconfig)
		if err != nil {
			grohl.Report(err, grohl.Data{"haystack_enpdoint": innerconfig.HaystackEndpoint})
		} else {
			grohl.SetErrorReporter(reporter)
		}
	}

	grohl.AddContext("app", innerconfig.App)
	grohl.AddContext("deploy", innerconfig.Env)
	grohl.AddContext("sha", innerconfig.Sha)

	var logger grohl.Logger
	if len(innerconfig.SyslogAddr) > 0 {
		writer, err := newSyslogWriter(innerconfig.SyslogAddr, innerconfig.App)
		if err == nil {
			logger = grohl.NewIoLogger(writer)
		}
	}

	if logger == nil {
		logger = grohl.NewIoLogger(nil)
	}

	go grohl.Watch(logger, logch)
}

func newSyslogWriter(configAddr, tag string) (*syslog.Writer, error) {
	net, addr, err := parseAddr(configAddr)
	if err != nil {
		return nil, err
	}
	writer, err := syslog.Dial(net, addr, syslog.LOG_INFO|syslog.LOG_LOCAL7, tag)
	if err != nil {
		grohl.Report(err, grohl.Data{"syslog_network": net, "syslog_addr": addr})
		fmt.Fprintf(os.Stderr, "Error opening syslog connection: %s\n", err)
	}
	return writer, err
}

func parseAddr(s string) (string, string, error) {
	u, err := url.Parse(s)
	if err != nil {
		return "", "", err
	}

	if u.Host == "" {
		return u.Scheme, u.Path, nil
	}
	return u.Scheme, u.Host, nil
}

func SendPeriodicStats(duration string, config ConfigWrapper, callback func(keyprefix string)) error {
	innerconfig := config.OpstocatConfiguration()
	if !innerconfig.ShowPeriodicStats() {
		return nil
	}

	dur, err := time.ParseDuration(duration)
	if err != nil {
		return err
	}

	keyprefix := fmt.Sprintf("sys.%s.", innerconfig.Hostname)
	if callback == nil {
		callback = nopPeriodicCallback
	}

	go sendPeriodicStats(dur, keyprefix, callback)
	return nil
}

func sendPeriodicStats(dur time.Duration, keyprefix string, callback func(keyprefix string)) {
	var memStats runtime.MemStats
	for {
		time.Sleep(dur)
		grohl.Gauge(1.0, keyprefix+"goroutines", grohl.Format(runtime.NumGoroutine()))

		runtime.ReadMemStats(&memStats)
		grohl.Gauge(1.0, keyprefix+"memory.alloc", grohl.Format(memStats.Alloc))
		grohl.Gauge(1.0, keyprefix+"memory.heap", grohl.Format(memStats.HeapAlloc))
		grohl.Gauge(1.0, keyprefix+"memory.stack", grohl.Format(memStats.StackInuse))
		grohl.Gauge(1.0, keyprefix+"memory.sys", grohl.Format(memStats.Sys))

		callback(keyprefix)
	}
}

func nopPeriodicCallback(keyprefix string) {}

func PrefixedStatter(prefix string, statter g2s.Statter) g2s.Statter {
	if prefix == "" {
		return statter
	}

	return &PrefixStatter{prefix, statter}
}

type PrefixStatter struct {
	Prefix  string
	Statter g2s.Statter
}

func (s *PrefixStatter) Counter(sampleRate float32, bucket string, n ...int) {
	s.Statter.Counter(sampleRate, fmt.Sprintf("%s.%s", s.Prefix, bucket), n...)
}

func (s *PrefixStatter) Timing(sampleRate float32, bucket string, d ...time.Duration) {
	s.Statter.Timing(sampleRate, fmt.Sprintf("%s.%s", s.Prefix, bucket), d...)
}

func (s *PrefixStatter) Gauge(sampleRate float32, bucket string, value ...string) {
	s.Statter.Gauge(sampleRate, fmt.Sprintf("%s.%s", s.Prefix, bucket), value...)
}

type NoOpStatter struct{}

func (s *NoOpStatter) Counter(sampleRate float32, bucket string, n ...int)          {}
func (s *NoOpStatter) Timing(sampleRate float32, bucket string, d ...time.Duration) {}
func (s *NoOpStatter) Gauge(sampleRate float32, bucket string, value ...string)     {}
<commit_msg>Calculate some gc stats The first stat is a gauge showing the number of times the garbage collector has run since the last sample.<commit_after>package opstocat

import (
	"fmt"
	"github.com/peterbourgon/g2s"
	"github.com/technoweenie/grohl"
	"log/syslog"
	"net/url"
	"os"
	"runtime"
	"time"
)

func SetupLogger(config ConfigWrapper) {
	innerconfig := config.OpstocatConfiguration()
	logch := make(chan grohl.Data, 100)
	chlogger, _ := grohl.NewChannelLogger(logch)
	grohl.SetLogger(chlogger)

	if len(innerconfig.StatsDAddress) > 0 {
		if innerconfig.StatsDAddress == "noop" {
			grohl.CurrentStatter = &NoOpStatter{}
		} else {
			statter, err := g2s.Dial("udp", innerconfig.StatsDAddress)
			if err != nil {
				grohl.Report(err, grohl.Data{"statsd_address": innerconfig.StatsDAddress})
				grohl.CurrentStatter = &NoOpStatter{}
			} else {
				grohl.CurrentStatter = statter
			}
		}
	}

	grohl.CurrentStatter = PrefixedStatter(innerconfig.App, grohl.CurrentStatter)

	if len(innerconfig.HaystackEndpoint) > 0 {
		reporter, err := NewHaystackReporter(innerconfig)
		if err != nil {
			grohl.Report(err, grohl.Data{"haystack_enpdoint": innerconfig.HaystackEndpoint})
		} else {
			grohl.SetErrorReporter(reporter)
		}
	}

	grohl.AddContext("app", innerconfig.App)
	grohl.AddContext("deploy", innerconfig.Env)
	grohl.AddContext("sha", innerconfig.Sha)

	var logger grohl.Logger
	if len(innerconfig.SyslogAddr) > 0 {
		writer, err := newSyslogWriter(innerconfig.SyslogAddr, innerconfig.App)
		if err == nil {
			logger = grohl.NewIoLogger(writer)
		}
	}

	if logger == nil {
		logger = grohl.NewIoLogger(nil)
	}

	go grohl.Watch(logger, logch)
}

func newSyslogWriter(configAddr, tag string) (*syslog.Writer, error) {
	net, addr, err := parseAddr(configAddr)
	if err != nil {
		return nil, err
	}
	writer, err := syslog.Dial(net, addr, syslog.LOG_INFO|syslog.LOG_LOCAL7, tag)
	if err != nil {
		grohl.Report(err, grohl.Data{"syslog_network": net, "syslog_addr": addr})
		fmt.Fprintf(os.Stderr, "Error opening syslog connection: %s\n", err)
	}
	return writer, err
}

func parseAddr(s string) (string, string, error) {
	u, err := url.Parse(s)
	if err != nil {
		return "", "", err
	}

	if u.Host == "" {
		return u.Scheme, u.Path, nil
	}
	return u.Scheme, u.Host, nil
}

func SendPeriodicStats(duration string, config ConfigWrapper, callback func(keyprefix string)) error {
	innerconfig := config.OpstocatConfiguration()
	if !innerconfig.ShowPeriodicStats() {
		return nil
	}

	dur, err := time.ParseDuration(duration)
	if err != nil {
		return err
	}

	keyprefix := fmt.Sprintf("sys.%s.", innerconfig.Hostname)
	if callback == nil {
		callback = nopPeriodicCallback
	}

	go sendPeriodicStats(dur, keyprefix, callback)
	return nil
}

func sendPeriodicStats(dur time.Duration, keyprefix string, callback func(keyprefix string)) {
	var memStats runtime.MemStats
	var lastGcCount uint32

	for {
		time.Sleep(dur)
		grohl.Gauge(1.0, keyprefix+"goroutines", grohl.Format(runtime.NumGoroutine()))

		runtime.ReadMemStats(&memStats)
		grohl.Gauge(1.0, keyprefix+"memory.alloc", grohl.Format(memStats.Alloc))
		grohl.Gauge(1.0, keyprefix+"memory.heap", grohl.Format(memStats.HeapAlloc))
		grohl.Gauge(1.0, keyprefix+"memory.stack", grohl.Format(memStats.StackInuse))
		grohl.Gauge(1.0, keyprefix+"memory.sys", grohl.Format(memStats.Sys))

		// Number of GCs since the last sample
		countGc := memStats.NumGC - lastGcCount
		grohl.Gauge(1.0, keyprefix+"memory.gc")

		if countGc > 0 {
			if countGc > 256 {
				countGc = 256
			}

			for i := uint32(0); i < countGc; i++ {
				idx := ((memStats.NumGC - i) + 255) % 256
				pause := time.Duration(memStats.PauseNs[idx])
				grohl.Timing(1.0, keyprefix+"memory.gc_pause", pause)
			}
		}

		lastGcCount = memStats.NumGC

		callback(keyprefix)
	}
}

func nopPeriodicCallback(keyprefix string) {}

func PrefixedStatter(prefix string, statter g2s.Statter) g2s.Statter {
	if prefix == "" {
		return statter
	}

	return &PrefixStatter{prefix, statter}
}

type PrefixStatter struct {
	Prefix  string
	Statter g2s.Statter
}

func (s *PrefixStatter) Counter(sampleRate float32, bucket string, n ...int) {
	s.Statter.Counter(sampleRate, fmt.Sprintf("%s.%s", s.Prefix, bucket), n...)
}

func (s *PrefixStatter) Timing(sampleRate float32, bucket string, d ...time.Duration) {
	s.Statter.Timing(sampleRate, fmt.Sprintf("%s.%s", s.Prefix, bucket), d...)
}

func (s *PrefixStatter) Gauge(sampleRate float32, bucket string, value ...string) {
	s.Statter.Gauge(sampleRate, fmt.Sprintf("%s.%s", s.Prefix, bucket), value...)
}

type NoOpStatter struct{}

func (s *NoOpStatter) Counter(sampleRate float32, bucket string, n ...int)          {}
func (s *NoOpStatter) Timing(sampleRate float32, bucket string, d ...time.Duration) {}
func (s *NoOpStatter) Gauge(sampleRate float32, bucket string, value ...string)     {}
<|endoftext|>


<commit_before>// Copyright 2015 The Cockroach Authors.
//
// 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. See the AUTHORS file
// for names of contributors.
//
// Author: Peter Mattis (peter@cockroachlabs.com)

package sql_test

import (
	"bufio"
	"bytes"
	"crypto/md5"
	"database/sql"
	"flag"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"reflect"
	"regexp"
	"strconv"
	"strings"
	"testing"
	"time"

	"github.com/cockroachdb/cockroach/server"
	"github.com/cockroachdb/cockroach/testutils"
	"github.com/cockroachdb/cockroach/util/leaktest"
)

var (
	resultsRE = regexp.MustCompile(`^(\d+)\s+values?\s+hashing\s+to\s+([0-9A-Fa-f]+)$`)
	errorRE   = regexp.MustCompile(`^(?:statement|query)\s+error\s+(.*)$`)
	testdata  = flag.String("d", "testdata/*", "test data glob")
)

type lineScanner struct {
	*bufio.Scanner
	line int
}

func newLineScanner(r io.Reader) *lineScanner {
	return &lineScanner{
		Scanner: bufio.NewScanner(r),
		line:    0,
	}
}

func (l *lineScanner) Scan() bool {
	ok := l.Scanner.Scan()
	if ok {
		l.line++
	}
	return ok
}

type logicStatement struct {
	pos       string
	sql       string
	expectErr string
}

type logicQuery struct {
	pos             string
	sql             string
	colNames        bool
	colTypes        string // TODO(pmattis): not (yet) implemented.
	label           string // TODO(pmattis): not (yet) implemented.
	expectErr       string
	expectedValues  int
	expectedHash    string
	expectedResults []string
}

// TODO(pmattis): #1961 is adding a similar type to cli/sql.go. Perhaps move
// this type into the sql or sql/driver packages and export it so that it can
// be shared.
type logicValue string

func (v *logicValue) Scan(value interface{}) error {
	switch t := value.(type) {
	case nil:
		*v = "NULL"
	case bool:
		*v = logicValue(strconv.FormatBool(t))
	case int64:
		*v = logicValue(strconv.FormatInt(t, 10))
	case float64:
		*v = logicValue(strconv.FormatFloat(t, 'g', -1, 64))
	case []byte:
		*v = logicValue(t)
	case string:
		*v = logicValue(t)
	case time.Time:
		*v = logicValue(t.String())
	default:
		return fmt.Errorf("unexpected type: %T", value)
	}
	return nil
}

// logicTest executes the test cases specified in a file. The file format is
// taken from the sqllogictest tool
// (http://www.sqlite.org/sqllogictest/doc/trunk/about.wiki) with various
// extensions to allow specifying errors and additional options. See
// https://github.com/gregrahn/sqllogictest/ for a github mirror of the
// sqllogictest source.
//
// TODO(pmattis): We currently cannot run the tests from sqllogictest due to
// insufficient SQL coverage (e.g. lack of subqueries and aggregation
// functions). We should work towards fixing that.
type logicTest struct {
	*testing.T
}

func (t logicTest) run(path string) {
	file, err := os.Open(path)
	if err != nil {
		t.Fatal(err)
	}
	defer file.Close()
	base := filepath.Base(path)

	// TODO(pmattis): Add a flag to make it easy to run the tests against a local
	// MySQL or Postgres instance.
	srv := server.StartTestServer(nil)

	// TODO(marc): Allow the user to be specified somehow so that we can
	// test permissions.
	db, err := sql.Open("cockroach", "https://root@"+srv.ServingAddr()+"?certs=test_certs")
	if err != nil {
		t.Fatal(err)
	}
	defer db.Close()
	defer srv.Stop()

	if _, err := db.Exec("CREATE DATABASE test"); err != nil {
		t.Fatal(err)
	}
	if _, err := db.Exec("SET DATABASE = test"); err != nil {
		t.Fatal(err)
	}

	s := newLineScanner(file)
	for s.Scan() {
		fields := strings.Fields(s.Text())
		if len(fields) == 0 {
			continue
		}
		cmd := fields[0]
		if strings.HasPrefix(cmd, "#") {
			// Skip comment lines.
			continue
		}
		switch cmd {
		case "statement":
			stmt := logicStatement{pos: fmt.Sprintf("%s:%d", base, s.line)}
			// Parse "query error <regexp>"
			if m := errorRE.FindStringSubmatch(s.Text()); m != nil {
				stmt.expectErr = m[1]
			}
			var buf bytes.Buffer
			for s.Scan() {
				line := s.Text()
				if line == "" {
					break
				}
				fmt.Fprintln(&buf, line)
			}
			stmt.sql = strings.TrimSpace(buf.String())
			t.execStatement(db, stmt)

		case "query":
			query := logicQuery{pos: fmt.Sprintf("%s:%d", base, s.line)}
			// Parse "query error <regexp>"
			if m := errorRE.FindStringSubmatch(s.Text()); m != nil {
				query.expectErr = m[1]
			} else if len(fields) < 2 {
				t.Fatalf("%s: invalid test statement: %s", query.pos, s.Text())
			} else {
				// TODO(pmattis): Parse "query <type-string> <sort-mode> <label>". The
				// type string specifies the number of columns and their types: T for
				// text, I for integer and R for floating point. The sort mode is one
				// of "nosort", "rowsort" or "valuesort". The default is "nosort".
				//
				// The label is optional. If specified, the test runner stores a hash
				// of the results of the query under the given label. If the label is
				// reused, the test runner verifieds that the results are the
				// same. This can be used to verify that two or more queries in the
				// same test script that are logically equivalent always generate the
				// same output.
				query.colTypes = fields[1]
				if len(fields) >= 3 {
					for _, opt := range strings.Split(fields[2], ",") {
						switch opt {
						// TODO(pmattis): The sort options are not yet implemented.
						case "nosort":
						case "rowsort":
						case "valuesort":

						case "colnames":
							query.colNames = true
						}
					}
				}
				if len(fields) >= 4 {
					query.label = fields[3]
				}
			}
			var buf bytes.Buffer
			for s.Scan() {
				line := s.Text()
				if line == "----" {
					break
				}
				fmt.Fprintln(&buf, line)
			}
			query.sql = strings.TrimSpace(buf.String())

			// Query results are either a space separated list of values up to a
			// blank line or a line of the form "xx values hashing to yyy". The
			// latter format is used by sqllogictest when a large number of results
			// match the query.
			if s.Scan() {
				if m := resultsRE.FindStringSubmatch(s.Text()); m != nil {
					var err error
					query.expectedValues, err = strconv.Atoi(m[1])
					if err != nil {
						t.Fatal(err)
					}
					query.expectedHash = m[2]
				} else {
					for {
						results := strings.Fields(s.Text())
						if len(results) == 0 {
							break
						}
						query.expectedResults = append(query.expectedResults, results...)
						if !s.Scan() {
							break
						}
					}
					query.expectedValues = len(query.expectedResults)
				}
			}

			t.execQuery(db, query)

		case "halt":
			break

		case "skipif", "onlyif":
			t.Fatalf("unimplemented test statement: %s", s.Text())
		}
	}

	if err := s.Err(); err != nil {
		t.Fatal(err)
	}
}

func (t logicTest) execStatement(db *sql.DB, stmt logicStatement) {
	fmt.Printf("%s: %s\n", stmt.pos, stmt.sql)
	_, err := db.Exec(stmt.sql)
	switch {
	case stmt.expectErr == "":
		if err != nil {
			t.Fatalf("%s: expected success, but found %v", stmt.pos, err)
		}
	case !testutils.IsError(err, stmt.expectErr):
		t.Fatalf("%s: expected %q, but found %q", stmt.pos, stmt.expectErr, err)
	}
}

func (t logicTest) execQuery(db *sql.DB, query logicQuery) {
	fmt.Printf("%s: %s\n", query.pos, query.sql)
	rows, err := db.Query(query.sql)
	if query.expectErr == "" {
		if err != nil {
			t.Fatalf("%s: expected success, but found %v", query.pos, err)
		}
	} else if !testutils.IsError(err, query.expectErr) {
		t.Fatalf("%s: expected %s, but found %v", query.pos, query.expectErr, err)
	} else {
		// An error occurred, but it was expected.
		return
	}
	defer rows.Close()

	cols, err := rows.Columns()
	if err != nil {
		t.Fatal(err)
	}
	vals := make([]interface{}, len(cols))
	for i := range vals {
		vals[i] = new(logicValue)
	}

	var results []string
	if query.colNames {
		results = append(results, cols...)
	}
	for rows.Next() {
		if err := rows.Scan(vals...); err != nil {
			t.Fatal(err)
		}
		for _, v := range vals {
			results = append(results, string(*v.(*logicValue)))
		}
	}
	if err := rows.Err(); err != nil {
		t.Fatal(err)
	}

	if query.expectedHash != "" {
		n := len(results)
		if query.expectedValues != n {
			t.Fatalf("%s: expected %d results, but found %d", query.pos, query.expectedValues, n)
		}
		// Hash the values using MD5. This hashing precisely matches the hashing in
		// sqllogictest.c.
		h := md5.New()
		for _, r := range results {
			_, _ = io.WriteString(h, r)
			_, _ = io.WriteString(h, "\n")
		}
		hash := fmt.Sprintf("%x", h.Sum(nil))
		if query.expectedHash != hash {
			t.Fatalf("%s: expected %s, but found %s", query.pos, query.expectedHash, hash)
		}
	} else if !reflect.DeepEqual(query.expectedResults, results) {
		t.Fatalf("%s: expected %q, but found %q\n", query.pos, query.expectedResults, results)
	}
}

func TestLogic(t *testing.T) {
	defer leaktest.AfterTest(t)

	l := logicTest{T: t}
	paths, err := filepath.Glob(*testdata)
	if err != nil {
		t.Fatal(err)
	}
	for _, p := range paths {
		l.run(p)
	}
}
<commit_msg>Add support for the query sort options.<commit_after>// Copyright 2015 The Cockroach Authors.
//
// 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. See the AUTHORS file
// for names of contributors.
//
// Author: Peter Mattis (peter@cockroachlabs.com)

package sql_test

import (
	"bufio"
	"bytes"
	"crypto/md5"
	"database/sql"
	"flag"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"reflect"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"testing"
	"time"

	"github.com/cockroachdb/cockroach/server"
	"github.com/cockroachdb/cockroach/testutils"
	"github.com/cockroachdb/cockroach/util/leaktest"
)

var (
	resultsRE = regexp.MustCompile(`^(\d+)\s+values?\s+hashing\s+to\s+([0-9A-Fa-f]+)$`)
	errorRE   = regexp.MustCompile(`^(?:statement|query)\s+error\s+(.*)$`)
	testdata  = flag.String("d", "testdata/*", "test data glob")
)

type lineScanner struct {
	*bufio.Scanner
	line int
}

func newLineScanner(r io.Reader) *lineScanner {
	return &lineScanner{
		Scanner: bufio.NewScanner(r),
		line:    0,
	}
}

func (l *lineScanner) Scan() bool {
	ok := l.Scanner.Scan()
	if ok {
		l.line++
	}
	return ok
}

type logicStatement struct {
	pos       string
	sql       string
	expectErr string
}

type logicSorter func(numCols int, values []string)

type rowSorter struct {
	numCols int
	numRows int
	values  []string
}

func (r rowSorter) row(i int) []string {
	return r.values[i*r.numCols : (i+1)*r.numCols]
}

func (r rowSorter) Len() int {
	return r.numRows
}

func (r rowSorter) Less(i, j int) bool {
	a := r.row(i)
	b := r.row(j)
	for k := range a {
		if a[k] < b[k] {
			return true
		}
		if a[k] > b[k] {
			return false
		}
	}
	return false
}

func (r rowSorter) Swap(i, j int) {
	a := r.row(i)
	b := r.row(j)
	for i := range a {
		a[i], b[i] = b[i], a[i]
	}
}

func rowSort(numCols int, values []string) {
	sort.Sort(rowSorter{
		numCols: numCols,
		numRows: len(values) / numCols,
		values:  values,
	})
}

func valueSort(numCols int, values []string) {
	sort.Strings(values)
}

type logicQuery struct {
	pos             string
	sql             string
	colNames        bool
	colTypes        string // TODO(pmattis): not (yet) implemented.
	label           string // TODO(pmattis): not (yet) implemented.
	sorter          logicSorter
	expectErr       string
	expectedValues  int
	expectedHash    string
	expectedResults []string
}

// TODO(pmattis): #1961 is adding a similar type to cli/sql.go. Perhaps move
// this type into the sql or sql/driver packages and export it so that it can
// be shared.
type logicValue string

func (v *logicValue) Scan(value interface{}) error {
	switch t := value.(type) {
	case nil:
		*v = "NULL"
	case bool:
		*v = logicValue(strconv.FormatBool(t))
	case int64:
		*v = logicValue(strconv.FormatInt(t, 10))
	case float64:
		*v = logicValue(strconv.FormatFloat(t, 'g', -1, 64))
	case []byte:
		*v = logicValue(t)
	case string:
		*v = logicValue(t)
	case time.Time:
		*v = logicValue(t.String())
	default:
		return fmt.Errorf("unexpected type: %T", value)
	}
	return nil
}

// logicTest executes the test cases specified in a file. The file format is
// taken from the sqllogictest tool
// (http://www.sqlite.org/sqllogictest/doc/trunk/about.wiki) with various
// extensions to allow specifying errors and additional options. See
// https://github.com/gregrahn/sqllogictest/ for a github mirror of the
// sqllogictest source.
//
// TODO(pmattis): We currently cannot run the tests from sqllogictest due to
// insufficient SQL coverage (e.g. lack of subqueries and aggregation
// functions). We should work towards fixing that.
type logicTest struct {
	*testing.T
}

func (t logicTest) run(path string) {
	file, err := os.Open(path)
	if err != nil {
		t.Fatal(err)
	}
	defer file.Close()
	base := filepath.Base(path)

	// TODO(pmattis): Add a flag to make it easy to run the tests against a local
	// MySQL or Postgres instance.
	srv := server.StartTestServer(nil)

	// TODO(marc): Allow the user to be specified somehow so that we can
	// test permissions.
	db, err := sql.Open("cockroach", "https://root@"+srv.ServingAddr()+"?certs=test_certs")
	if err != nil {
		t.Fatal(err)
	}
	defer db.Close()
	defer srv.Stop()

	if _, err := db.Exec("CREATE DATABASE test"); err != nil {
		t.Fatal(err)
	}
	if _, err := db.Exec("SET DATABASE = test"); err != nil {
		t.Fatal(err)
	}

	s := newLineScanner(file)
	for s.Scan() {
		fields := strings.Fields(s.Text())
		if len(fields) == 0 {
			continue
		}
		cmd := fields[0]
		if strings.HasPrefix(cmd, "#") {
			// Skip comment lines.
			continue
		}
		switch cmd {
		case "statement":
			stmt := logicStatement{pos: fmt.Sprintf("%s:%d", base, s.line)}
			// Parse "query error <regexp>"
			if m := errorRE.FindStringSubmatch(s.Text()); m != nil {
				stmt.expectErr = m[1]
			}
			var buf bytes.Buffer
			for s.Scan() {
				line := s.Text()
				if line == "" {
					break
				}
				fmt.Fprintln(&buf, line)
			}
			stmt.sql = strings.TrimSpace(buf.String())
			t.execStatement(db, stmt)

		case "query":
			query := logicQuery{pos: fmt.Sprintf("%s:%d", base, s.line)}
			// Parse "query error <regexp>"
			if m := errorRE.FindStringSubmatch(s.Text()); m != nil {
				query.expectErr = m[1]
			} else if len(fields) < 2 {
				t.Fatalf("%s: invalid test statement: %s", query.pos, s.Text())
			} else {
				// TODO(pmattis): Parse "query <type-string> <sort-mode> <label>". The
				// type string specifies the number of columns and their types: T for
				// text, I for integer and R for floating point. The sort mode is one
				// of "nosort", "rowsort" or "valuesort". The default is "nosort".
				//
				// The label is optional. If specified, the test runner stores a hash
				// of the results of the query under the given label. If the label is
				// reused, the test runner verifieds that the results are the
				// same. This can be used to verify that two or more queries in the
				// same test script that are logically equivalent always generate the
				// same output.
				query.colTypes = fields[1]
				if len(fields) >= 3 {
					for _, opt := range strings.Split(fields[2], ",") {
						switch opt {
						// TODO(pmattis): The sort options are not yet implemented.
						case "nosort":
							query.sorter = nil

						case "rowsort":
							query.sorter = rowSort

						case "valuesort":
							query.sorter = valueSort

						case "colnames":
							query.colNames = true
						}
					}
				}
				if len(fields) >= 4 {
					query.label = fields[3]
				}
			}
			var buf bytes.Buffer
			for s.Scan() {
				line := s.Text()
				if line == "----" {
					break
				}
				fmt.Fprintln(&buf, line)
			}
			query.sql = strings.TrimSpace(buf.String())

			// Query results are either a space separated list of values up to a
			// blank line or a line of the form "xx values hashing to yyy". The
			// latter format is used by sqllogictest when a large number of results
			// match the query.
			if s.Scan() {
				if m := resultsRE.FindStringSubmatch(s.Text()); m != nil {
					var err error
					query.expectedValues, err = strconv.Atoi(m[1])
					if err != nil {
						t.Fatal(err)
					}
					query.expectedHash = m[2]
				} else {
					for {
						results := strings.Fields(s.Text())
						if len(results) == 0 {
							break
						}
						query.expectedResults = append(query.expectedResults, results...)
						if !s.Scan() {
							break
						}
					}
					query.expectedValues = len(query.expectedResults)
				}
			}

			t.execQuery(db, query)

		case "halt":
			break

		case "skipif", "onlyif":
			t.Fatalf("unimplemented test statement: %s", s.Text())
		}
	}

	if err := s.Err(); err != nil {
		t.Fatal(err)
	}
}

func (t logicTest) execStatement(db *sql.DB, stmt logicStatement) {
	fmt.Printf("%s: %s\n", stmt.pos, stmt.sql)
	_, err := db.Exec(stmt.sql)
	switch {
	case stmt.expectErr == "":
		if err != nil {
			t.Fatalf("%s: expected success, but found %v", stmt.pos, err)
		}
	case !testutils.IsError(err, stmt.expectErr):
		t.Fatalf("%s: expected %q, but found %q", stmt.pos, stmt.expectErr, err)
	}
}

func (t logicTest) execQuery(db *sql.DB, query logicQuery) {
	fmt.Printf("%s: %s\n", query.pos, query.sql)
	rows, err := db.Query(query.sql)
	if query.expectErr == "" {
		if err != nil {
			t.Fatalf("%s: expected success, but found %v", query.pos, err)
		}
	} else if !testutils.IsError(err, query.expectErr) {
		t.Fatalf("%s: expected %s, but found %v", query.pos, query.expectErr, err)
	} else {
		// An error occurred, but it was expected.
		return
	}
	defer rows.Close()

	cols, err := rows.Columns()
	if err != nil {
		t.Fatal(err)
	}
	vals := make([]interface{}, len(cols))
	for i := range vals {
		vals[i] = new(logicValue)
	}

	var results []string
	if query.colNames {
		results = append(results, cols...)
	}
	for rows.Next() {
		if err := rows.Scan(vals...); err != nil {
			t.Fatal(err)
		}
		for _, v := range vals {
			results = append(results, string(*v.(*logicValue)))
		}
	}
	if err := rows.Err(); err != nil {
		t.Fatal(err)
	}

	if query.sorter != nil {
		query.sorter(len(cols), results)
	}

	if query.expectedHash != "" {
		n := len(results)
		if query.expectedValues != n {
			t.Fatalf("%s: expected %d results, but found %d", query.pos, query.expectedValues, n)
		}
		// Hash the values using MD5. This hashing precisely matches the hashing in
		// sqllogictest.c.
		h := md5.New()
		for _, r := range results {
			_, _ = io.WriteString(h, r)
			_, _ = io.WriteString(h, "\n")
		}
		hash := fmt.Sprintf("%x", h.Sum(nil))
		if query.expectedHash != hash {
			t.Fatalf("%s: expected %s, but found %s", query.pos, query.expectedHash, hash)
		}
	} else if !reflect.DeepEqual(query.expectedResults, results) {
		t.Fatalf("%s: expected %q, but found %q\n", query.pos, query.expectedResults, results)
	}
}

func TestLogic(t *testing.T) {
	defer leaktest.AfterTest(t)

	l := logicTest{T: t}
	paths, err := filepath.Glob(*testdata)
	if err != nil {
		t.Fatal(err)
	}
	for _, p := range paths {
		l.run(p)
	}
}
<|endoftext|>


<commit_before>package client

import (
	"encoding/json"
	"fmt"
	"net/http"
	"strconv"
	"strings"
	"time"
	"io/ioutil"
	"bytes"
)

// NomadServer is connection parameters to a nomad server
type NomadServer struct {
	Address string
	Port    int
}

// Details represents a json object in a nomad job
type Details struct {
	Running int `json:"running"`
}

// Summary represents a json object in a nomad job
type Summary struct {
	Details *Details `json:"server"`
}

// JobSummary represents a json object in a nomad job
type JobSummary struct {
	Summary *Summary `json:"Summary"`
}

// Job is a representation of nomad job
type Job struct {
	Name       string      `json:"Name"`
	Priority   int         `json:"Priority"`
	Status     string      `json:"status"`
	JobSummary *JobSummary `json:"JobSummary"`
}

// Task represents an allocated nomad task
type Task struct {
	State string `json:"State"`
}

// Alloc is a representation of a nomad allocation
type Alloc struct {
	ID           string          `json:"ID"`
	JobID        string          `json:"JobID"`
	NodeID       string          `json:"NodeID"`
	Name         string          `json:"Name"`
	ClientStatus string          `json:"ClientStatus"`
	Tasks        map[string]Task `json:"TaskStates"`
}

// Host is a representation of a nomad client node
type Host struct {
	ID    string `json:"ID"`
	Name  string `json:"Name"`
	Drain bool   `json:"Drain"`
}

// JobNotFound indicates that a Job search failed
type JobNotFound struct {
	Name string
}

// AllocNotFound indicates a missing allocation for a Job
type AllocNotFound struct {
	Jobname  string
	Hostname string
}

var httpClient = &http.Client{Timeout: 5 * time.Second}

// Jobs will parse the json representation from the nomad rest api
// /v1/jobs
func Jobs(nomad *NomadServer) []Job {
	jobs := make([]Job, 0)
	decodeJSON(url(nomad)+"/v1/jobs", &jobs)
	return jobs
}

// FindJob will parse the json representation and find the supplied job name
func FindJob(nomad *NomadServer, name string) (*Job, error) {
	jobs := Jobs(nomad)
	for _, job := range jobs {
		if job.Name == name {
			return &job, nil
		}
	}
	return &Job{}, &JobNotFound{Name: name}
}

func (e *JobNotFound) Error() string {
	return fmt.Sprintf("Unable to find job name: %v", e.Name)
}

// Hosts will parse the json representation from the nomad rest api
// /v1/nodes
func Hosts(nomad *NomadServer) []Host {
	hosts := make([]Host, 0)
	decodeJSON(url(nomad)+"/v1/nodes", &hosts)
	return hosts
}

// Drain will inform nomad to add/remove all allocations from that host
// depending on the value of enable
func Drain(nomad *NomadServer, id string, enable bool) string {
	resp, _ := httpClient.Post(url(nomad)+"/v1/node/"+id+"/drain?enable="+strconv.FormatBool(enable), "application/json", nil)
	defer resp.Body.Close()
	return resp.Status
}

func SubmitJob(nomad *NomadServer, launchFilePath string) (string, error) {
    file, err := ioutil.ReadFile(launchFilePath)
    resp, _ := httpClient.Post(url(nomad)+"/v1/jobs", "application/json", bytes.NewBuffer(file))
    defer resp.Body.Close()
    return resp.Status, err
}

// Allocs will parse the json representation from the nomad rest api
// /v1/allocations
func Allocs(nomad *NomadServer) []Alloc {
	allocs := make([]Alloc, 0)
	decodeJSON(url(nomad)+"/v1/allocations", &allocs)
	return allocs
}

// FindAlloc will search through the Allocs on a provided Host to look for the
// allocations that match the provided Job
func FindAlloc(nomad *NomadServer, job *Job, host *Host) (*Alloc, error) {
	allocs := Allocs(nomad)
	for _, alloc := range allocs {
		if alloc.NodeID == host.ID && strings.Contains(alloc.Name, job.Name) {
			return &alloc, nil
		}
	}
	return &Alloc{}, &AllocNotFound{Hostname: host.Name, Jobname: job.Name}
}

// CheckTaskStates will return whether all of the tasks for an Alloc are
// in the state provided
func (alloc *Alloc) CheckTaskStates(state string) bool {
	for k := range alloc.Tasks {
		task := alloc.Tasks[k]
		if task.State != state {
			return false
		}
	}
	return true
}

func (e *AllocNotFound) Error() string {
	return fmt.Sprintf("Unable to find '%v' job on '%v' host.", e.Jobname, e.Hostname)
}

func url(nomad *NomadServer) string {
	return fmt.Sprintf("http://%v:%v", nomad.Address, nomad.Port)
}

func decodeJSON(url string, target interface{}) error {
	r, err := httpClient.Get(url)
	if err != nil {
		return err
	}
	defer r.Body.Close()
	return json.NewDecoder(r.Body).Decode(target)
}
<commit_msg>Fixing segmentation fault in case of Nomad not being up.  Also adding more robust error handling in the post methods (drain & submit job)<commit_after>package client

import (
	"encoding/json"
	"fmt"
	"net/http"
	"strconv"
	"strings"
	"time"
	"io/ioutil"
	"bytes"
)

const http_bad_payload string = "400"
const http_unknown_error string = "520"

// NomadServer is connection parameters to a nomad server
type NomadServer struct {
	Address string
	Port    int
}

// Details represents a json object in a nomad job
type Details struct {
	Running int `json:"running"`
}

// Summary represents a json object in a nomad job
type Summary struct {
	Details *Details `json:"server"`
}

// JobSummary represents a json object in a nomad job
type JobSummary struct {
	Summary *Summary `json:"Summary"`
}

// Job is a representation of nomad job
type Job struct {
	Name       string      `json:"Name"`
	Priority   int         `json:"Priority"`
	Status     string      `json:"status"`
	JobSummary *JobSummary `json:"JobSummary"`
}

// Task represents an allocated nomad task
type Task struct {
	State string `json:"State"`
}

// Alloc is a representation of a nomad allocation
type Alloc struct {
	ID           string          `json:"ID"`
	JobID        string          `json:"JobID"`
	NodeID       string          `json:"NodeID"`
	Name         string          `json:"Name"`
	ClientStatus string          `json:"ClientStatus"`
	Tasks        map[string]Task `json:"TaskStates"`
}

// Host is a representation of a nomad client node
type Host struct {
	ID    string `json:"ID"`
	Name  string `json:"Name"`
	Drain bool   `json:"Drain"`
}

// JobNotFound indicates that a Job search failed
type JobNotFound struct {
	Name string
}

// AllocNotFound indicates a missing allocation for a Job
type AllocNotFound struct {
	Jobname  string
	Hostname string
}

var httpClient = &http.Client{Timeout: 5 * time.Second}

// Jobs will parse the json representation from the nomad rest api
// /v1/jobs
func Jobs(nomad *NomadServer) []Job {
	jobs := make([]Job, 0)
	decodeJSON(url(nomad)+"/v1/jobs", &jobs)
	return jobs
}

// FindJob will parse the json representation and find the supplied job name
func FindJob(nomad *NomadServer, name string) (*Job, error) {
	jobs := Jobs(nomad)
	for _, job := range jobs {
		if job.Name == name {
			return &job, nil
		}
	}
	return &Job{}, &JobNotFound{Name: name}
}

func (e *JobNotFound) Error() string {
	return fmt.Sprintf("Unable to find job name: %v", e.Name)
}

// Hosts will parse the json representation from the nomad rest api
// /v1/nodes
func Hosts(nomad *NomadServer) []Host {
	hosts := make([]Host, 0)
	decodeJSON(url(nomad)+"/v1/nodes", &hosts)
	return hosts
}

// Drain will inform nomad to add/remove all allocations from that host
// depending on the value of enable
func Drain(nomad *NomadServer, id string, enable bool) (string, error) {
	resp, err := httpClient.Post(url(nomad)+"/v1/node/"+id+"/drain?enable="+strconv.FormatBool(enable), "application/json", nil)
	if resp != nil && resp.Body != nil {
	        defer resp.Body.Close()
	        return resp.Status, err
	}
	return http_unknown_error, err
}

func SubmitJob(nomad *NomadServer, launchFilePath string) (string, error) {
    file, err := ioutil.ReadFile(launchFilePath)
    if err != nil {
        return http_bad_payload, err
    }
    resp, err := httpClient.Post(url(nomad)+"/v1/jobs", "application/json", bytes.NewBuffer(file))
    if resp != nil && resp.Body != nil {
            defer resp.Body.Close()
            return resp.Status, err
    }
    return http_unknown_error, err
}

// Allocs will parse the json representation from the nomad rest api
// /v1/allocations
func Allocs(nomad *NomadServer) []Alloc {
	allocs := make([]Alloc, 0)
	decodeJSON(url(nomad)+"/v1/allocations", &allocs)
	return allocs
}

// FindAlloc will search through the Allocs on a provided Host to look for the
// allocations that match the provided Job
func FindAlloc(nomad *NomadServer, job *Job, host *Host) (*Alloc, error) {
	allocs := Allocs(nomad)
	for _, alloc := range allocs {
		if alloc.NodeID == host.ID && strings.Contains(alloc.Name, job.Name) {
			return &alloc, nil
		}
	}
	return &Alloc{}, &AllocNotFound{Hostname: host.Name, Jobname: job.Name}
}

// CheckTaskStates will return whether all of the tasks for an Alloc are
// in the state provided
func (alloc *Alloc) CheckTaskStates(state string) bool {
	for k := range alloc.Tasks {
		task := alloc.Tasks[k]
		if task.State != state {
			return false
		}
	}
	return true
}

func (e *AllocNotFound) Error() string {
	return fmt.Sprintf("Unable to find '%v' job on '%v' host.", e.Jobname, e.Hostname)
}

func url(nomad *NomadServer) string {
	return fmt.Sprintf("http://%v:%v", nomad.Address, nomad.Port)
}

func decodeJSON(url string, target interface{}) error {
	r, err := httpClient.Get(url)
	if err != nil {
		return err
	}
	defer r.Body.Close()
	return json.NewDecoder(r.Body).Decode(target)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net"
	"net/http"
	"os"
	"strings"
	"time"

	"github.com/codegangsta/cli"
)

const VERSION = "0.2"

var CONFIG string

func main() {
	CONFIG = fmt.Sprintf("%v/groku", os.TempDir())
	app := cli.NewApp()
	app.Name = "groku"
	app.Version = VERSION
	app.Usage = "roku CLI remote"
	app.Commands = commands()
	app.Run(os.Args)
}

func findRoku() string {
	fi, err := os.Open(CONFIG)
	defer fi.Close()
	if err != nil {
		ssdp, _ := net.ResolveUDPAddr("udp", "239.255.255.250:1900")
		addr, _ := net.ResolveUDPAddr("udp", ":0")
		socket, _ := net.ListenUDP("udp", addr)

		socket.WriteToUDP([]byte("M-SEARCH * HTTP/1.1\r\n"+
			"HOST: 239.255.255.250:1900\r\n"+
			"MAN: \"ssdp:discover\"\r\n"+
			"ST: roku:ecp\r\n"+
			"MX: 3 \r\n\r\n"), ssdp)

		answerBytes := make([]byte, 1024)
		socket.SetReadDeadline(time.Now().Add(3 * time.Second))
		_, _, err := socket.ReadFromUDP(answerBytes[:])
		if err != nil {
			fmt.Println("Could not find your Roku!")
			os.Exit(1)
		}

		ret := strings.Split(string(answerBytes), "\r\n")
		location := strings.TrimPrefix(ret[len(ret)-3], "LOCATION: ")

		fi, err := os.Create(CONFIG)
		defer fi.Close()
		if err != nil {
			return location
		}
		fi.Write([]byte(location))
		return location
	}
	buf := make([]byte, 1024)
	n, err := fi.Read(buf[:])
	if err != nil {
		os.Remove(CONFIG)
		return findRoku()
	} else {
		return string(buf[:n])
	}
}

func commands() []cli.Command {
	cmds := []cli.Command{}
	for _, cmd := range []string{
		"home",
		"rev",
		"fwd",
		"select",
		"left",
		"right",
		"down",
		"up",
		"back",
		"info",
		"backspace",
		"enter",
		"search",
	} {
		cmds = append(cmds, cli.Command{
			Name:  cmd,
			Usage: cmd,
			Action: func(c *cli.Context) {
				http.PostForm(fmt.Sprintf("%vkeypress/%v", findRoku(), c.Command.Name), nil)
			},
		})
	}
	cmds = append(cmds, cli.Command{
		Name:  "replay",
		Usage: "replay",
		Action: func(c *cli.Context) {
			http.PostForm(fmt.Sprintf("%vkeypress/%v", findRoku(), "InstantReplay"), nil)
		},
	})
	cmds = append(cmds, cli.Command{
		Name:  "play",
		Usage: "play/pause",
		Action: func(c *cli.Context) {
			http.PostForm(fmt.Sprintf("%vkeypress/%v", findRoku(), "Play"), nil)
		},
	})
	cmds = append(cmds, cli.Command{
		Name:  "discover",
		Usage: "discover roku on your local network",
		Action: func(c *cli.Context) {
			os.Remove(CONFIG)
			fmt.Println("Found roku at", findRoku())
		},
	})
	cmds = append(cmds, cli.Command{
		Name:  "text",
		Usage: "send text to the roku",
		Action: func(c *cli.Context) {
			roku := findRoku()
			for _, c := range c.Args()[0] {
				http.PostForm(fmt.Sprintf("%vkeypress/Lit_%v", roku, string(c)), nil)
			}
		},
	})
	return cmds
}
<commit_msg>Add support for querying and launching roku apps<commit_after>package main

import (
	"encoding/xml"
	"fmt"
	"log"
	"net"
	"net/http"
	"os"
	"strings"
	"time"

	"github.com/codegangsta/cli"
)

const VERSION = "0.3"

var CONFIG string

type dictonary struct {
	XMLName xml.Name `xml:"apps"`
	Apps    []app    `xml:"app"`
}

type app struct {
	Name string `xml:",chardata"`
	ID   string `xml:"id,attr"`
}

func main() {
	CONFIG = fmt.Sprintf("%v/groku", os.TempDir())
	app := cli.NewApp()
	app.Name = "groku"
	app.Version = VERSION
	app.Usage = "roku CLI remote"
	app.Commands = commands()
	app.Run(os.Args)
}

func queryApps() dictonary {
	resp, _ := http.Get(fmt.Sprintf("%vquery/apps", findRoku()))
	body := make([]byte, 2048)
	n, _ := resp.Body.Read(body)

	var dict dictonary
	if err := xml.Unmarshal(body[:n], &dict); err != nil {
		log.Fatalln(err)
	}
	return dict
}

func findRoku() string {
	fi, err := os.Open(CONFIG)
	defer fi.Close()
	if err != nil {
		ssdp, _ := net.ResolveUDPAddr("udp", "239.255.255.250:1900")
		addr, _ := net.ResolveUDPAddr("udp", ":0")
		socket, _ := net.ListenUDP("udp", addr)

		socket.WriteToUDP([]byte("M-SEARCH * HTTP/1.1\r\n"+
			"HOST: 239.255.255.250:1900\r\n"+
			"MAN: \"ssdp:discover\"\r\n"+
			"ST: roku:ecp\r\n"+
			"MX: 3 \r\n\r\n"), ssdp)

		answerBytes := make([]byte, 1024)
		socket.SetReadDeadline(time.Now().Add(3 * time.Second))
		_, _, err := socket.ReadFromUDP(answerBytes[:])
		if err != nil {
			fmt.Println("Could not find your Roku!")
			os.Exit(1)
		}

		ret := strings.Split(string(answerBytes), "\r\n")
		location := strings.TrimPrefix(ret[len(ret)-3], "LOCATION: ")

		fi, err := os.Create(CONFIG)
		defer fi.Close()
		if err != nil {
			return location
		}
		fi.Write([]byte(location))
		return location
	}
	buf := make([]byte, 1024)
	n, err := fi.Read(buf[:])
	if err != nil {
		os.Remove(CONFIG)
		return findRoku()
	} else {
		return string(buf[:n])
	}
}

func commands() []cli.Command {
	cmds := []cli.Command{}
	for _, cmd := range []string{
		"home",
		"rev",
		"fwd",
		"select",
		"left",
		"right",
		"down",
		"up",
		"back",
		"info",
		"backspace",
		"enter",
		"search",
	} {
		cmds = append(cmds, cli.Command{
			Name:  cmd,
			Usage: cmd,
			Action: func(c *cli.Context) {
				http.PostForm(fmt.Sprintf("%vkeypress/%v", findRoku(), c.Command.Name), nil)
			},
		})
	}
	cmds = append(cmds, cli.Command{
		Name:  "replay",
		Usage: "replay",
		Action: func(c *cli.Context) {
			http.PostForm(fmt.Sprintf("%vkeypress/%v", findRoku(), "InstantReplay"), nil)
		},
	})
	cmds = append(cmds, cli.Command{
		Name:  "play",
		Usage: "play/pause",
		Action: func(c *cli.Context) {
			http.PostForm(fmt.Sprintf("%vkeypress/%v", findRoku(), "Play"), nil)
		},
	})
	cmds = append(cmds, cli.Command{
		Name:  "discover",
		Usage: "discover roku on your local network",
		Action: func(c *cli.Context) {
			os.Remove(CONFIG)
			fmt.Println("Found roku at", findRoku())
		},
	})
	cmds = append(cmds, cli.Command{
		Name:  "text",
		Usage: "send text to the roku",
		Action: func(c *cli.Context) {
			roku := findRoku()
			for _, c := range c.Args()[0] {
				http.PostForm(fmt.Sprintf("%vkeypress/Lit_%v", roku, string(c)), nil)
			}
		},
	})
	cmds = append(cmds, cli.Command{
		Name:  "apps",
		Usage: "list installed apps on roku",
		Action: func(c *cli.Context) {
			dict := queryApps()
			fmt.Println("Installed apps:")
			for _, a := range dict.Apps {
				fmt.Println(a.Name)
			}
		},
	})
	cmds = append(cmds, cli.Command{
		Name:  "app",
		Usage: "launch specified app",
		Action: func(c *cli.Context) {
			dict := queryApps()
			for _, a := range dict.Apps {
				if a.Name == c.Args()[0] {
					http.PostForm(fmt.Sprintf("%vlaunch/%v", findRoku(), a.ID), nil)
					return
				}
			}
			fmt.Println("App not found!")
			os.Exit(1)
		},
	})
	return cmds
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package main

// CmdUser subcommand
import (
	"errors"
	"os"
	"strconv"

	"github.com/fatih/color"
	"github.com/olekukonko/tablewriter"
	"github.com/urfave/cli"
)

// DeleteGroup ...
var DeleteGroup = cli.Command{
	Name:      "delete",
	Usage:     "Deletes a group.",
	ArgsUsage: " ",
	Description: `Deletes a group by name

	  Example:
		  $ ernest group delete <name>
	`,
	Action: func(c *cli.Context) error {
		if len(c.Args()) < 1 {
			msg := "You should specify the group name"
			color.Red(msg)
			return errors.New(msg)
		}

		m, cfg := setup(c)
		if cfg.Token == "" {
			color.Red("You're not allowed to perform this action, please log in")
			return nil
		}
		session, err := m.getSession(cfg.Token)
		if err != nil {
			color.Red("You don't have permissions to perform this action")
			return nil
		}

		if session.IsAdmin == false {
			color.Red("You don't have permissions to perform this action")
			return nil
		}

		name := c.Args()[0]
		err = m.DeleteGroup(cfg.Token, c.Args()[0])
		if err != nil {
			color.Red(err.Error())
			return nil
		}
		color.Green("Group '" + name + "' successfully deleted")
		return nil
	},
}

// RemoveDatacenter ...
var RemoveDatacenter = cli.Command{
	Name:      "remove-datacenter",
	Usage:     "Removes a datacenter from a group.",
	ArgsUsage: " ",
	Description: `Removes an datacenter from a group.

		Example:
		  $ ernest group remove-datacenter <datacenter-id> <group-id>
	`,
	Action: func(c *cli.Context) error {
		if len(c.Args()) < 1 {
			msg := "You should specify the datacenter name and group name"
			color.Red(msg)
			return errors.New(msg)
		}
		if len(c.Args()) < 2 {
			msg := "You should specify the group name"
			color.Red(msg)
			return errors.New(msg)
		}

		m, cfg := setup(c)
		if cfg.Token == "" {
			color.Red("You're not allowed to perform this action, please log in")
			return nil
		}
		session, err := m.getSession(cfg.Token)
		if err != nil {
			color.Red("You don't have permissions to perform this action")
			return nil
		}

		if session.IsAdmin == false {
			color.Red("You don't have permissions to perform this action")
			return nil
		}

		datacenter := c.Args()[0]
		group := c.Args()[1]
		err = m.GroupRemoveDatacenter(cfg.Token, datacenter, group)
		if err != nil {
			color.Red(err.Error())
			return nil
		}
		color.Green("Datacenter '" + datacenter + "' is not assigned anymore to group '" + group + "'")
		return nil
	},
}

// AddDatacenter ...
var AddDatacenter = cli.Command{
	Name:      "add-datacenter",
	Usage:     "Adds a datacenter to a group.",
	ArgsUsage: " ",
	Description: `Adds a datacenter to a group.

	  Example:
		  $ ernest group add-datacenter <datacenter-name> <group-name>
	`,
	Action: func(c *cli.Context) error {
		if len(c.Args()) < 1 {
			msg := "You should specify the datacenter name and group name"
			color.Red(msg)
			return errors.New(msg)
		}
		if len(c.Args()) < 2 {
			msg := "You should specify the group name"
			color.Red(msg)
			return errors.New(msg)
		}

		m, cfg := setup(c)
		if cfg.Token == "" {
			color.Red("You're not allowed to perform this action, please log in")
			return nil
		}
		session, err := m.getSession(cfg.Token)
		if err != nil {
			color.Red("You don't have permissions to perform this action")
			return nil
		}

		if session.IsAdmin == false {
			color.Red("You don't have permissions to perform this action")
			return nil
		}

		datacenter := c.Args()[0]
		group := c.Args()[1]
		err = m.GroupAddDatacenter(cfg.Token, datacenter, group)
		if err != nil {
			color.Red(err.Error())
			return nil
		}
		color.Green("Datacenter '" + datacenter + "' is now assigned to group '" + group + "'")
		return nil
	},
}

// RemoveUser ...
var RemoveUser = cli.Command{
	Name:      "remove-user",
	Usage:     "Removes an user from a group.",
	ArgsUsage: " ",
	Description: `Removes an user from a group.

		Example:
		  $ ernest group remove-user <user-id> <group-id>
	`,
	Action: func(c *cli.Context) error {
		if len(c.Args()) < 1 {
			msg := "You should specify the username and group name"
			color.Red(msg)
			return errors.New(msg)
		}
		if len(c.Args()) < 2 {
			msg := "You should specify the group name"
			color.Red(msg)
			return errors.New(msg)
		}

		m, cfg := setup(c)
		if cfg.Token == "" {
			color.Red("You're not allowed to perform this action, please log in")
			return nil
		}
		session, err := m.getSession(cfg.Token)
		if err != nil {
			color.Red("You don't have permissions to perform this action")
			return nil
		}

		if session.IsAdmin == false {
			color.Red("You don't have permissions to perform this action")
			return nil
		}

		user := c.Args()[0]
		group := c.Args()[1]
		err = m.GroupRemoveUser(cfg.Token, user, group)
		if err != nil {
			color.Red(err.Error())
			return nil
		}
		color.Green("User '" + user + "' is not assigned anymore to group '" + group + "'")
		return nil
	},
}

// AddUser : Adds a user to a group
var AddUser = cli.Command{
	Name:      "add-user",
	Usage:     "Adds a user to a group.",
	ArgsUsage: " ",
	Description: `Adds a user to a group.

	  Example:
		  $ ernest group add-user <user-name> <group-name>
	`,
	Action: func(c *cli.Context) error {
		if len(c.Args()) < 1 {
			msg := "You should specify the username and group name"
			color.Red(msg)
			return errors.New(msg)
		}
		if len(c.Args()) < 2 {
			msg := "You should specify the group name"
			color.Red(msg)
			return errors.New(msg)
		}

		m, cfg := setup(c)
		if cfg.Token == "" {
			color.Red("You're not allowed to perform this action, please log in")
			return nil
		}
		session, err := m.getSession(cfg.Token)
		if err != nil {
			color.Red("You don't have permissions to perform this action")
			return nil
		}

		if session.IsAdmin == false {
			color.Red("You don't have permissions to perform this action")
			return nil
		}

		user := c.Args()[0]
		group := c.Args()[1]
		err = m.GroupAddUser(cfg.Token, user, group)
		if err != nil {
			color.Red(err.Error())
			return nil
		}
		color.Green("User '" + user + "' is now assigned to group '" + group + "'")
		return nil
	},
}

// CreateGroup : Creates a group
var CreateGroup = cli.Command{
	Name:      "create",
	Usage:     "Create a group.",
	ArgsUsage: " ",
	Description: `Create a group.

   Example:
    $ ernest group create <name>
	`,
	Action: func(c *cli.Context) error {
		if len(c.Args()) < 1 {
			color.Red("You should specify the group name")
			return nil
		}

		m, cfg := setup(c)
		if cfg.Token == "" {
			color.Red("You're not allowed to perform this action, please log in")
			return nil
		}
		session, err := m.getSession(cfg.Token)
		if err != nil {
			color.Red("You don't have permissions to perform this action")
			return nil
		}

		if session.IsAdmin == false {
			color.Red("You don't have permissions to perform this action")
			return nil
		}

		name := c.Args()[0]
		err = m.CreateGroup(cfg.Token, c.Args()[0])
		if err != nil {
			color.Red(err.Error())
			return nil
		}
		color.Green("Group '" + name + "' successfully created, you can add users with 'ernest group add-user username " + name + "'")
		return nil
	},
}

// ListGroups ...
var ListGroups = cli.Command{
	Name:      "list",
	Usage:     "List available groups.",
	ArgsUsage: " ",
	Description: `List available groups.

   Example:
    $ ernest group list
	`,
	Action: func(c *cli.Context) error {
		m, cfg := setup(c)
		if cfg.Token == "" {
			color.Red("You're not allowed to perform this action, please log in")
			return nil
		}
		groups, err := m.ListGroups(cfg.Token)
		if err != nil {
			color.Red("We didn't found any accessible group")
			return nil
		}

		table := tablewriter.NewWriter(os.Stdout)
		table.SetHeader([]string{"ID", "Name"})
		for _, g := range groups {
			id := strconv.Itoa(g.ID)
			table.Append([]string{id, g.Name})
		}
		table.Render()

		return nil
	},
}

// CmdGroup ...
var CmdGroup = cli.Command{
	Name:  "group",
	Usage: "Group related subcommands",
	Subcommands: []cli.Command{
		DeleteGroup,
		ListGroups,
		CreateGroup,
		AddUser,
		RemoveUser,
		AddDatacenter,
		RemoveDatacenter,
	},
}
<commit_msg>Fix doc<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package main

// CmdUser subcommand
import (
	"errors"
	"os"
	"strconv"

	"github.com/fatih/color"
	"github.com/olekukonko/tablewriter"
	"github.com/urfave/cli"
)

// DeleteGroup ...
var DeleteGroup = cli.Command{
	Name:      "delete",
	Usage:     "Deletes a group.",
	ArgsUsage: " ",
	Description: `Deletes a group by name

	  Example:
		  $ ernest group delete <name>
	`,
	Action: func(c *cli.Context) error {
		if len(c.Args()) < 1 {
			msg := "You should specify the group name"
			color.Red(msg)
			return errors.New(msg)
		}

		m, cfg := setup(c)
		if cfg.Token == "" {
			color.Red("You're not allowed to perform this action, please log in")
			return nil
		}
		session, err := m.getSession(cfg.Token)
		if err != nil {
			color.Red("You don't have permissions to perform this action")
			return nil
		}

		if session.IsAdmin == false {
			color.Red("You don't have permissions to perform this action")
			return nil
		}

		name := c.Args()[0]
		err = m.DeleteGroup(cfg.Token, c.Args()[0])
		if err != nil {
			color.Red(err.Error())
			return nil
		}
		color.Green("Group '" + name + "' successfully deleted")
		return nil
	},
}

// RemoveDatacenter ...
var RemoveDatacenter = cli.Command{
	Name:      "remove-datacenter",
	Usage:     "Removes a datacenter from a group.",
	ArgsUsage: " ",
	Description: `Removes an datacenter from a group.

		Example:
		  $ ernest group remove-datacenter <datacenter name> <group name>
	`,
	Action: func(c *cli.Context) error {
		if len(c.Args()) < 1 {
			msg := "You should specify the datacenter name and group name"
			color.Red(msg)
			return errors.New(msg)
		}
		if len(c.Args()) < 2 {
			msg := "You should specify the group name"
			color.Red(msg)
			return errors.New(msg)
		}

		m, cfg := setup(c)
		if cfg.Token == "" {
			color.Red("You're not allowed to perform this action, please log in")
			return nil
		}
		session, err := m.getSession(cfg.Token)
		if err != nil {
			color.Red("You don't have permissions to perform this action")
			return nil
		}

		if session.IsAdmin == false {
			color.Red("You don't have permissions to perform this action")
			return nil
		}

		datacenter := c.Args()[0]
		group := c.Args()[1]
		err = m.GroupRemoveDatacenter(cfg.Token, datacenter, group)
		if err != nil {
			color.Red(err.Error())
			return nil
		}
		color.Green("Datacenter '" + datacenter + "' is not assigned anymore to group '" + group + "'")
		return nil
	},
}

// AddDatacenter ...
var AddDatacenter = cli.Command{
	Name:      "add-datacenter",
	Usage:     "Adds a datacenter to a group.",
	ArgsUsage: " ",
	Description: `Adds a datacenter to a group.

	  Example:
		  $ ernest group add-datacenter <datacenter-name> <group-name>
	`,
	Action: func(c *cli.Context) error {
		if len(c.Args()) < 1 {
			msg := "You should specify the datacenter name and group name"
			color.Red(msg)
			return errors.New(msg)
		}
		if len(c.Args()) < 2 {
			msg := "You should specify the group name"
			color.Red(msg)
			return errors.New(msg)
		}

		m, cfg := setup(c)
		if cfg.Token == "" {
			color.Red("You're not allowed to perform this action, please log in")
			return nil
		}
		session, err := m.getSession(cfg.Token)
		if err != nil {
			color.Red("You don't have permissions to perform this action")
			return nil
		}

		if session.IsAdmin == false {
			color.Red("You don't have permissions to perform this action")
			return nil
		}

		datacenter := c.Args()[0]
		group := c.Args()[1]
		err = m.GroupAddDatacenter(cfg.Token, datacenter, group)
		if err != nil {
			color.Red(err.Error())
			return nil
		}
		color.Green("Datacenter '" + datacenter + "' is now assigned to group '" + group + "'")
		return nil
	},
}

// RemoveUser ...
var RemoveUser = cli.Command{
	Name:      "remove-user",
	Usage:     "Removes an user from a group.",
	ArgsUsage: " ",
	Description: `Removes an user from a group.

		Example:
		  $ ernest group remove-user <username> <group name>
	`,
	Action: func(c *cli.Context) error {
		if len(c.Args()) < 1 {
			msg := "You should specify the username and group name"
			color.Red(msg)
			return errors.New(msg)
		}
		if len(c.Args()) < 2 {
			msg := "You should specify the group name"
			color.Red(msg)
			return errors.New(msg)
		}

		m, cfg := setup(c)
		if cfg.Token == "" {
			color.Red("You're not allowed to perform this action, please log in")
			return nil
		}
		session, err := m.getSession(cfg.Token)
		if err != nil {
			color.Red("You don't have permissions to perform this action")
			return nil
		}

		if session.IsAdmin == false {
			color.Red("You don't have permissions to perform this action")
			return nil
		}

		user := c.Args()[0]
		group := c.Args()[1]
		err = m.GroupRemoveUser(cfg.Token, user, group)
		if err != nil {
			color.Red(err.Error())
			return nil
		}
		color.Green("User '" + user + "' is not assigned anymore to group '" + group + "'")
		return nil
	},
}

// AddUser : Adds a user to a group
var AddUser = cli.Command{
	Name:      "add-user",
	Usage:     "Adds a user to a group.",
	ArgsUsage: " ",
	Description: `Adds a user to a group.

	  Example:
		  $ ernest group add-user <user-name> <group-name>
	`,
	Action: func(c *cli.Context) error {
		if len(c.Args()) < 1 {
			msg := "You should specify the username and group name"
			color.Red(msg)
			return errors.New(msg)
		}
		if len(c.Args()) < 2 {
			msg := "You should specify the group name"
			color.Red(msg)
			return errors.New(msg)
		}

		m, cfg := setup(c)
		if cfg.Token == "" {
			color.Red("You're not allowed to perform this action, please log in")
			return nil
		}
		session, err := m.getSession(cfg.Token)
		if err != nil {
			color.Red("You don't have permissions to perform this action")
			return nil
		}

		if session.IsAdmin == false {
			color.Red("You don't have permissions to perform this action")
			return nil
		}

		user := c.Args()[0]
		group := c.Args()[1]
		err = m.GroupAddUser(cfg.Token, user, group)
		if err != nil {
			color.Red(err.Error())
			return nil
		}
		color.Green("User '" + user + "' is now assigned to group '" + group + "'")
		return nil
	},
}

// CreateGroup : Creates a group
var CreateGroup = cli.Command{
	Name:      "create",
	Usage:     "Create a group.",
	ArgsUsage: " ",
	Description: `Create a group.

   Example:
    $ ernest group create <name>
	`,
	Action: func(c *cli.Context) error {
		if len(c.Args()) < 1 {
			color.Red("You should specify the group name")
			return nil
		}

		m, cfg := setup(c)
		if cfg.Token == "" {
			color.Red("You're not allowed to perform this action, please log in")
			return nil
		}
		session, err := m.getSession(cfg.Token)
		if err != nil {
			color.Red("You don't have permissions to perform this action")
			return nil
		}

		if session.IsAdmin == false {
			color.Red("You don't have permissions to perform this action")
			return nil
		}

		name := c.Args()[0]
		err = m.CreateGroup(cfg.Token, c.Args()[0])
		if err != nil {
			color.Red(err.Error())
			return nil
		}
		color.Green("Group '" + name + "' successfully created, you can add users with 'ernest group add-user username " + name + "'")
		return nil
	},
}

// ListGroups ...
var ListGroups = cli.Command{
	Name:      "list",
	Usage:     "List available groups.",
	ArgsUsage: " ",
	Description: `List available groups.

   Example:
    $ ernest group list
	`,
	Action: func(c *cli.Context) error {
		m, cfg := setup(c)
		if cfg.Token == "" {
			color.Red("You're not allowed to perform this action, please log in")
			return nil
		}
		groups, err := m.ListGroups(cfg.Token)
		if err != nil {
			color.Red("We didn't found any accessible group")
			return nil
		}

		table := tablewriter.NewWriter(os.Stdout)
		table.SetHeader([]string{"ID", "Name"})
		for _, g := range groups {
			id := strconv.Itoa(g.ID)
			table.Append([]string{id, g.Name})
		}
		table.Render()

		return nil
	},
}

// CmdGroup ...
var CmdGroup = cli.Command{
	Name:  "group",
	Usage: "Group related subcommands",
	Subcommands: []cli.Command{
		DeleteGroup,
		ListGroups,
		CreateGroup,
		AddUser,
		RemoveUser,
		AddDatacenter,
		RemoveDatacenter,
	},
}
<|endoftext|>


<commit_before>package gitgo

import (
	"reflect"
	"strings"
	"testing"
)

func Test_CatFile(t *testing.T) {
	const inputSha = SHA("97eed02ebe122df8fdd853c1215d8775f3d9f1a1")
	const expected = "commit 190\x00" + `tree 9de6c72106b169990a83ce7090c7cad84b6b506b
author aditya <dev@chimeracoder.net> 1428075900 -0400
committer aditya <dev@chimeracoder.net> 1428075900 -0400

First commit. Create .gitignore`
	result, err := CatFile(inputSha)
	if err != nil {
		t.Error(err)
		return
	}

	if strings.Trim(result, "\n\r") != strings.Trim(expected, "\n\r") {

		t.Errorf("Expected and result don't match:\n%s \n\n\nresult: \n%s", expected, result)
	}
}

func Test_parseObjInitialCommit(t *testing.T) {
	expected := Commit{
		_type:     "commit",
		Tree:      "9de6c72106b169990a83ce7090c7cad84b6b506b",
		Parents:   nil,
		Author:    "aditya <dev@chimeracoder.net> 1428075900 -0400",
		Committer: "aditya <dev@chimeracoder.net> 1428075900 -0400",
		Message:   "First commit. Create .gitignore",
		size:      "190",
	}

	const input = "commit 190\x00" + `tree 9de6c72106b169990a83ce7090c7cad84b6b506b
author aditya <dev@chimeracoder.net> 1428075900 -0400
committer aditya <dev@chimeracoder.net> 1428075900 -0400

First commit. Create .gitignore`
	result, err := parseObj(input)
	if err != nil {
		t.Error(err)
		return
	}

	if !reflect.DeepEqual(expected, result) {
		t.Errorf("Expected and result don't match:\n%+v\n%+v", expected, result)
	}
}

func Test_parseObj(t *testing.T) {
	const inputSha = SHA("3ead3116d0378089f5ce61086354aac43e736b01")

	expected := Commit{
		_type:     "commit",
		Tree:      "d22fc8a57073fdecae2001d00aff921440d3aabd",
		Parents:   []string{"1d833eb5b6c5369c0cb7a4a3e20ded237490145f"},
		Author:    "aditya <dev@chimeracoder.net> 1428349896 -0400",
		Committer: "aditya <dev@chimeracoder.net> 1428349896 -0400",
		Message:   "Remove extraneous logging statements\n",
		size:      "243",
	}

	str, err := CatFile(inputSha)
	if err != nil {
		t.Error(err)
	}

	result, err := parseObj(str)
	if err != nil {
		t.Error(err)
		return
	}

	if !reflect.DeepEqual(expected, result) {
		t.Errorf("Expected and result don't match:\n%+v\n%+v", expected, result)
	}
}

func Test_ParseTree(t *testing.T) {
	const inputSha = SHA("1efecd717188441397c07f267cf468fdf04d4796")
	expected := Tree{
		_type: "tree",
		size:  "156",
		Blobs: []objectMeta{
			objectMeta{SHA("af6e4fe91a8f9a0f3c03cbec9e1d2aac47345d67"), "100644", ".gitignore"},
			objectMeta{SHA("f45d37d9add8f21eb84678f6d2c66377c4dd0c5e"), "100644", "cat-file.go"},
			objectMeta{SHA("2c225b962d6666011c69ca5c2c67204959f8ba32"), "100644", "cat-file_test.go"},
		},
		Trees: []objectMeta{
			objectMeta{SHA("d564d0bc3dd917926892c55e3706cc116d5b165e"), "040000", "examples"},
		},
	}
	result, err := NewObject(inputSha)
	if err != nil {
		t.Error(err)
	}
	if !reflect.DeepEqual(expected, result) {
		t.Errorf("Expected and result don't match:\n\n%+v\n\n%+v", expected, result)
	}

}

func Test_ParseBlob(t *testing.T) {
	const inputSha = SHA("af6e4fe91a8f9a0f3c03cbec9e1d2aac47345d67")
	expected := Blob{
		_type:    "blob",
		size:     "18",
		Contents: "*.swp\n*.swo\n*.swn\n",
	}
	result, err := NewObject(inputSha)
	if err != nil {
		t.Error(err)
	}
	if !reflect.DeepEqual(expected, result) {
		t.Errorf("Expected and result don't match:\n\n%+v\n\n%+v", expected, result)
	}

}
<commit_msg>writes failing test for verify-pack feature<commit_after>package gitgo

import (
	"path"
	"reflect"
	"testing"
)

func Test_GetIdxPath(t *testing.T) {
	var testDirPath = "test_data/dot_git/"
	result, err := getIdxPath(testDirPath)

	if err != nil {
		t.Error(err)
		return
	}

	expected := path.Join(
		testDirPath,
		"pack-d310969c4ba0ebfe725685fa577a1eec5ecb15b2.idx",
	)

	if !reflect.DeepEqual(expected, result) {
		t.Errorf("Expected and result don't match:\n%+v\n%+v", expected, result)
	}
}

// func Testk_CatFile(t *testing.T) {
// 	const inputSha = SHA("97eed02ebe122df8fdd853c1215d8775f3d9f1a1")
// 	const expected = "commit 190\x00" + `tree 9de6c72106b169990a83ce7090c7cad84b6b506b
// author aditya <dev@chimeracoder.net> 1428075900 -0400
// committer aditya <dev@chimeracoder.net> 1428075900 -0400
//
// First commit. Create .gitignore`
// 	result, err := CatFile(inputSha)
// 	if err != nil {
// 		t.Error(err)
// 		return
// 	}
//
// 	if strings.Trim(result, "\n\r") != strings.Trim(expected, "\n\r") {
//
// 		t.Errorf("Expected and result don't match:\n%s \n\n\nresult: \n%s", expected, result)
// 	}
// }
//
// func Test_parseObjInitialCommit(t *testing.T) {
// 	expected := Commit{
// 		_type:     "commit",
// 		Tree:      "9de6c72106b169990a83ce7090c7cad84b6b506b",
// 		Parents:   nil,
// 		Author:    "aditya <dev@chimeracoder.net> 1428075900 -0400",
// 		Committer: "aditya <dev@chimeracoder.net> 1428075900 -0400",
// 		Message:   "First commit. Create .gitignore",
// 		size:      "190",
// 	}
//
// 	const input = "commit 190\x00" + `tree 9de6c72106b169990a83ce7090c7cad84b6b506b
// author aditya <dev@chimeracoder.net> 1428075900 -0400
// committer aditya <dev@chimeracoder.net> 1428075900 -0400
//
// First commit. Create .gitignore`
// 	result, err := parseObj(input)
// 	if err != nil {
// 		t.Error(err)
// 		return
// 	}
//
// 	if !reflect.DeepEqual(expected, result) {
// 		t.Errorf("Expected and result don't match:\n%+v\n%+v", expected, result)
// 	}
// }
//
// func Test_parseObj(t *testing.T) {
// 	const inputSha = SHA("3ead3116d0378089f5ce61086354aac43e736b01")
//
// 	expected := Commit{
// 		_type:     "commit",
// 		Tree:      "d22fc8a57073fdecae2001d00aff921440d3aabd",
// 		Parents:   []string{"1d833eb5b6c5369c0cb7a4a3e20ded237490145f"},
// 		Author:    "aditya <dev@chimeracoder.net> 1428349896 -0400",
// 		Committer: "aditya <dev@chimeracoder.net> 1428349896 -0400",
// 		Message:   "Remove extraneous logging statements\n",
// 		size:      "243",
// 	}
//
// 	str, err := CatFile(inputSha)
// 	if err != nil {
// 		t.Error(err)
// 	}
//
// 	result, err := parseObj(str)
// 	if err != nil {
// 		t.Error(err)
// 		return
// 	}
//
// 	if !reflect.DeepEqual(expected, result) {
// 		t.Errorf("Expected and result don't match:\n%+v\n%+v", expected, result)
// 	}
// }
//
// func Test_ParseTree(t *testing.T) {
// 	const inputSha = SHA("1efecd717188441397c07f267cf468fdf04d4796")
// 	expected := Tree{
// 		_type: "tree",
// 		size:  "156",
// 		Blobs: []objectMeta{
// 			objectMeta{SHA("af6e4fe91a8f9a0f3c03cbec9e1d2aac47345d67"), "100644", ".gitignore"},
// 			objectMeta{SHA("f45d37d9add8f21eb84678f6d2c66377c4dd0c5e"), "100644", "cat-file.go"},
// 			objectMeta{SHA("2c225b962d6666011c69ca5c2c67204959f8ba32"), "100644", "cat-file_test.go"},
// 		},
// 		Trees: []objectMeta{
// 			objectMeta{SHA("d564d0bc3dd917926892c55e3706cc116d5b165e"), "040000", "examples"},
// 		},
// 	}
// 	result, err := NewObject(inputSha)
// 	if err != nil {
// 		t.Error(err)
// 	}
// 	if !reflect.DeepEqual(expected, result) {
// 		t.Errorf("Expected and result don't match:\n\n%+v\n\n%+v", expected, result)
// 	}
//
// }
//
// func Test_ParseBlob(t *testing.T) {
// 	const inputSha = SHA("af6e4fe91a8f9a0f3c03cbec9e1d2aac47345d67")
// 	expected := Blob{
// 		_type:    "blob",
// 		size:     "18",
// 		Contents: "*.swp\n*.swo\n*.swn\n",
// 	}
// 	result, err := NewObject(inputSha)
// 	if err != nil {
// 		t.Error(err)
// 	}
// 	if !reflect.DeepEqual(expected, result) {
// 		t.Errorf("Expected and result don't match:\n\n%+v\n\n%+v", expected, result)
// 	}
//
// }
<|endoftext|>


<commit_before>package main

import (
	"database/sql"
	"fmt"
	"log"

	_ "github.com/go-sql-driver/mysql"
)

var (
	stInsert *sql.Stmt
	stUpdate *sql.Stmt
	stDelete *sql.Stmt
)

func main() {
	db, err := sql.Open("mysql", "vagrant:db1234@tcp(127.0.0.1:3306)/vagrant")
	if err != nil {
		log.Fatal(err)
	}
	defer db.Close()
	err = test(db)
	if err != nil {
		log.Printf("WARN: %s", err)
	}
}

func test(db *sql.DB) error {
	if err := test0(db); err != nil {
		return err
	}
	if err := test1(db); err != nil {
		return err
	}
	if err := test2(db); err != nil {
		return err
	}
	if err := test3(db); err != nil {
		return err
	}
	// TODO:
	if err := test99(db); err != nil {
		return err
	}
	return nil
}

func test0(db *sql.DB) error {
	rows, err := db.Query(`SHOW DATABASES`)
	if err != nil {
		return err
	}
	defer rows.Close()
	for rows.Next() {
		var name string
		err := rows.Scan(&name)
		if err != nil {
			return err
		}
		fmt.Printf("table: %s\n", name)
	}
	return nil
}

func test1(db *sql.DB) error {
	_, err := db.Exec(`CREATE TABLE IF NOT EXISTS users (
		id INT PRIMARY KEY AUTO_INCREMENT,
		name VARCHAR(255) UNIQUE,
		password VARCHAR(255)
	)`)
	if err != nil {
		return err
	}
	return nil
}

func test2(db *sql.DB) error {
	var err error
	stInsert, err = db.Prepare(
		`INSERT INTO users (name, password) VALUES (?, ?)`)
	if err != nil {
		return err
	}
	stUpdate, err = db.Prepare(
		`UPDATE users SET name = ?, password = ? WHERE id = ?`)
	if err != nil {
		return err
	}
	stDelete, err = db.Prepare(`DELETE FROM users WHERE id = ?`)
	if err != nil {
		return err
	}
	return nil
}

func test3(db *sql.DB) error {
	var err error
	_, err = db.Prepare(
		`INSERT INTO users (name, password) VALUES (?, ?`)
	if err == nil {
		panic("prepare in test3 should be failed")
	}
	log.Printf("test3: %s", err)
	return nil
}

func test99(db *sql.DB) error {
	if stDelete != nil {
		stDelete.Close()
	}
	if stUpdate != nil {
		stUpdate.Close()
	}
	if stInsert != nil {
		stInsert.Close()
	}
	_, err := db.Exec(`DROP TABLE users`)
	if err != nil {
		return err
	}
	return nil
}
<commit_msg>better log<commit_after>package main

import (
	"database/sql"
	"fmt"
	"log"

	_ "github.com/go-sql-driver/mysql"
)

var (
	stInsert *sql.Stmt
	stUpdate *sql.Stmt
	stDelete *sql.Stmt
)

func main() {
	db, err := sql.Open("mysql", "vagrant:db1234@tcp(127.0.0.1:3306)/vagrant")
	if err != nil {
		log.Fatal(err)
	}
	defer db.Close()
	err = test(db)
	if err != nil {
		log.Printf("WARN: %s", err)
	}
}

func test(db *sql.DB) error {
	if err := test0(db); err != nil {
		return err
	}
	if err := test1(db); err != nil {
		return err
	}
	if err := test2(db); err != nil {
		return err
	}
	if err := test3(db); err != nil {
		return err
	}
	// TODO:
	if err := test99(db); err != nil {
		return err
	}
	return nil
}

func test0(db *sql.DB) error {
	rows, err := db.Query(`SHOW DATABASES`)
	if err != nil {
		return err
	}
	defer rows.Close()
	for rows.Next() {
		var name string
		err := rows.Scan(&name)
		if err != nil {
			return err
		}
		fmt.Printf("test0: table:%s found\n", name)
	}
	return nil
}

func test1(db *sql.DB) error {
	_, err := db.Exec(`CREATE TABLE IF NOT EXISTS users (
		id INT PRIMARY KEY AUTO_INCREMENT,
		name VARCHAR(255) UNIQUE,
		password VARCHAR(255)
	)`)
	if err != nil {
		return err
	}
	return nil
}

func test2(db *sql.DB) error {
	var err error
	stInsert, err = db.Prepare(
		`INSERT INTO users (name, password) VALUES (?, ?)`)
	if err != nil {
		return err
	}
	stUpdate, err = db.Prepare(
		`UPDATE users SET name = ?, password = ? WHERE id = ?`)
	if err != nil {
		return err
	}
	stDelete, err = db.Prepare(`DELETE FROM users WHERE id = ?`)
	if err != nil {
		return err
	}
	return nil
}

func test3(db *sql.DB) error {
	var err error
	_, err = db.Prepare(
		`INSERT INTO users (name, password) VALUES (?, ?`)
	if err == nil {
		panic("prepare in test3 should be failed")
	}
	fmt.Printf("test3: IGNORED ERROR: %s\n", err)
	return nil
}

func test99(db *sql.DB) error {
	if stDelete != nil {
		stDelete.Close()
	}
	if stUpdate != nil {
		stUpdate.Close()
	}
	if stInsert != nil {
		stInsert.Close()
	}
	_, err := db.Exec(`DROP TABLE users`)
	if err != nil {
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>package impl

import (
	"errors"
	"fmt"
	"os"
	"os/exec"
	"path"
	"sort"

	"veyron/lib/cmdline"
)

// Root returns a command that represents the root of the veyron tool.
func Root() *cmdline.Command {
	return &cmdline.Command{
		Name:  "veyron",
		Short: "Command-line tool for managing the veyron project",
		Long: `
The veyron tool facilitates interaction with the veyron project.
In particular, it can be used to install different veyron profiles.
`,
		Children: []*cmdline.Command{cmdSetup, cmdVersion},
	}
}

var (
	profiles = map[string]string{
		"android":           "Android veyron development",
		"cross-compilation": "cross-compilation for Linux/ARM",
		"developer":         "core veyron development",
	}
)

func profilesDescription() string {
	result := `
<profiles> is a list of profiles to set up. Currently, the veyron tool
supports the following profiles:
`
	sortedProfiles := make([]string, 0)
	maxLength := 0
	for profile, _ := range profiles {
		sortedProfiles = append(sortedProfiles, profile)
		if len(profile) > maxLength {
			maxLength = len(profile)
		}
	}
	sort.Strings(sortedProfiles)
	for _, profile := range sortedProfiles {
		result += fmt.Sprintf("  %*s: %s\n", maxLength, profile, profiles[profile])
	}
	return result
}

// cmdVersion represent the 'version' command of the veyron tool.
var cmdVersion = &cmdline.Command{
	Run:   runVersion,
	Name:  "version",
	Short: "Print version.",
	Long:  "Print version and commit hash used to build the veyron tool.",
}

const version string = "0.1.0"

// commitId should be over-written during build:
// go build -ldflags "-X tools/veyron/impl.commitId <commitId>" tools/veyron
var commitId string = "test-build"

func runVersion(cmd *cmdline.Command, args []string) error {
	fmt.Printf("%v (%v)\n", version, commitId)
	return nil
}

// cmdSetup represents the 'setup' command of the veyron tool.
var cmdSetup = &cmdline.Command{
	Run:   runSetup,
	Name:  "setup",
	Short: "Set up the given veyron profiles",
	Long: `
To facilitate development across different platforms, veyron defines
platform-independent profiles that map different platforms to a set
of libraries and tools that can be used for a factor of veyron
development. The "setup" command can be used to install the libraries
and tools identified by the combination of the given profiles and
the host platform.
`,
	ArgsName: "<profiles>",
	ArgsLong: profilesDescription(),
}

func runSetup(cmd *cmdline.Command, args []string) error {
	// Check that the profiles to be set up exist.
	for _, arg := range args {
		if _, ok := profiles[arg]; !ok {
			cmd.Errorf("Unknown profile '%s'", arg)
			return cmdline.ErrUsage
		}
	}
	// Setup the profiles.
	root := os.Getenv("VEYRON_ROOT")
	script := path.Join(root, "environment/scripts/setup/machine/init.sh")
	for _, arg := range args {
		checkpoints := os.Getenv("VEYRON_CHK")
		if err := os.MkdirAll(checkpoints, 0777); err != nil {
			return errors.New("checkpoint setup failed")
		}
		if err := os.Setenv("CHK_PREFIX", arg); err != nil {
			return errors.New("checkpoint setup failed")
		}
		if err := os.Setenv("CHK_COUNTER", "0"); err != nil {
			return errors.New("checkpoint setup failed")
		}
		cmd := exec.Command(script, "-p", arg)
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		if err := cmd.Run(); err != nil {
			return errors.New("profile setup failed")
		}
		if err := os.RemoveAll(checkpoints); err != nil {
			return errors.New("checkpoint setup failed")
		}
	}
	return nil
}
<commit_msg>go/src/tools/veyron: detecting supported profiles at runtime<commit_after>package impl

import (
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"runtime"

	"veyron/lib/cmdline"
)

// Root returns a command that represents the root of the veyron tool.
func Root() *cmdline.Command {
	return &cmdline.Command{
		Name:  "veyron",
		Short: "Command-line tool for managing the veyron project",
		Long: `
The veyron tool facilitates interaction with the veyron project.
In particular, it can be used to install different veyron profiles.
`,
		Children: []*cmdline.Command{cmdSetup, cmdVersion},
	}
}

func profilesDescription() string {
	result := "<profiles> is a list of profiles to set up. Supported profiles are:\n"
	root := os.Getenv("VEYRON_ROOT")
	if root == "" {
		panic("VEYRON_ROOT is not set.")
	}
	dir := path.Join(root, "environment/scripts/setup", runtime.GOOS)
	entries, err := ioutil.ReadDir(dir)
	if err != nil {
		panic(fmt.Sprintf("Could not read %s.", dir))
	}
	for _, entry := range entries {
		file := path.Join(dir, entry.Name(), "DESCRIPTION")
		description, err := ioutil.ReadFile(file)
		if err != nil {
			panic(fmt.Sprintf("Could not read %s.", file))
		}
		result += fmt.Sprintf("  %s: %s", entry.Name(), string(description))
	}
	return result
}

// cmdVersion represent the 'version' command of the veyron tool.
var cmdVersion = &cmdline.Command{
	Run:   runVersion,
	Name:  "version",
	Short: "Print version.",
	Long:  "Print version and commit hash used to build the veyron tool.",
}

const version string = "0.1.0"

// commitId should be over-written during build:
// go build -ldflags "-X tools/veyron/impl.commitId <commitId>" tools/veyron
var commitId string = "test-build"

func runVersion(cmd *cmdline.Command, args []string) error {
	fmt.Printf("%v (%v)\n", version, commitId)
	return nil
}

// cmdSetup represents the 'setup' command of the veyron tool.
var cmdSetup = &cmdline.Command{
	Run:   runSetup,
	Name:  "setup",
	Short: "Set up the given veyron profiles",
	Long: `
To facilitate development across different platforms, veyron defines
platform-independent profiles that map different platforms to a set
of libraries and tools that can be used for a factor of veyron
development. The "setup" command can be used to install the libraries
and tools identified by the combination of the given profiles and
the host platform.
`,
	ArgsName: "<profiles>",
	ArgsLong: profilesDescription(),
}

func runSetup(cmd *cmdline.Command, args []string) error {
	root := os.Getenv("VEYRON_ROOT")
	if root == "" {
		cmd.Errorf("VEYRON_ROOT is not set.")
	}
	// Check that the profiles to be set up exist.
	for _, arg := range args {
		script := path.Join(root, "environment/scripts/setup", runtime.GOOS, arg, "setup.sh")
		if _, err := os.Lstat(script); err != nil {
			cmd.Errorf("Unknown profile '%s'", arg)
			return cmdline.ErrUsage
		}
	}
	// Setup the profiles.
	for _, arg := range args {
		script := path.Join(root, "environment/scripts/setup", runtime.GOOS, arg, "setup.sh")
		cmd := exec.Command(script)
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		if err := cmd.Run(); err != nil {
			return errors.New("profile setup failed")
		}
	}
	return nil
}
<|endoftext|>