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

package io

import (
	"testing"

	"github.com/cpmech/gosl/chk"
)

func Test_strpair01(tst *testing.T) {

	//verbose()
	chk.PrintTitle("strpair01")

	str := "keyA: value :"
	key, val := ExtractStrPair(str, ":")
	Pforan("key = %q\n", key)
	Pforan("val = %q\n", val)
	if key != "keyA" {
		tst.Errorf("key %q is incorrect\n", key)
	}
	if val != "value" {
		tst.Errorf("val %q is incorrect\n", val)
	}
}

func Test_keycode01(tst *testing.T) {

	//verbose()
	chk.PrintTitle("keycode01")

	str := "!typeA:keycodeA !typeB:keycodeB!typeC  : keycodeC"
	if resA, ok := Keycode(str, "typeA"); !ok {
		tst.Errorf("cannot find   typeA")
	} else {
		if resA != "keycodeA" {
			tst.Errorf("resA != keycodeA")
		}
	}
	if resB, ok := Keycode(str, "typeB"); !ok {
		tst.Errorf("cannot find   typeB")
	} else {
		if resB != "keycodeB" {
			tst.Errorf("resB != keycodeB")
		}
	}
	if resC, ok := Keycode(str, "typeC"); !ok {
		tst.Errorf("cannot find   typeC")
	} else {
		if resC != "keycodeC" {
			tst.Errorf("resC != keycodeC")
		}
	}
	if resD, ok := Keycode(str, "typeD"); ok {
		tst.Errorf("must not find typeD")
	} else {
		if resD != "" {
			tst.Errorf("resD != \"\"    ")
		}
	}

	res, ok := Keycode("", "")
	if res != "" {
		tst.Errorf("error when handling empty string")
	}
	if ok {
		tst.Errorf("error when handling empty string")
	}

	res, ok = Keycode("!", "")
	if res != "" {
		tst.Errorf("error when handling '!' string")
	}
	if ok {
		tst.Errorf("error when handling '!' string")
	}

	res, ok = Keycode("!keyA !keyB", "keyA")
	if res != "" {
		tst.Errorf("error when handling '!keyA !keyB' string")
	}
	if !ok {
		tst.Errorf("error when handling '!keyA !keyB' string")
	}
}

func Test_keycode02(tst *testing.T) {

	//verbose()
	chk.PrintTitle("keycode02")

	str := "!flagA !typeA:keycodeA !typeB:keycodeB!typeC  : keycodeC !FlagB"
	res := Keycodes(str)
	chk.Strings(tst, "keycodes", res, []string{"flagA", "typeA", "typeB", "typeC", "FlagB"})
}

func Test_parsing02(tst *testing.T) {

	//verbose()
	chk.PrintTitle("parsing02")

	keys := []string{"ux", "uy", "uz", "pl", "sl"}
	skeys := JoinKeys(keys)
	kkeys := SplitKeys(skeys)
	Pforan("keys  = %+v\n", keys)
	Pforan("skeys = %q\n", skeys)
	Pforan("kkeys = %+v\n", kkeys)
	chk.Strings(tst, "keys", keys, kkeys)

	k1, k2 := []string{"ex1", "ex2"}, []string{"more1", "more2"}
	allkeys := JoinKeys3(keys, k1, k2, "")
	Pfpink("allkeys = %q\n", allkeys)
	chk.String(tst, allkeys, "ux uy uz pl sl ex1 ex2 more1 more2")

	allkeys = JoinKeys3(keys, k1, k2, ",")
	Pfpink("allkeys = %q\n", allkeys)
	chk.String(tst, allkeys, "ux uy uz pl sl, ex1 ex2, more1 more2")

	r0, r1, r2 := SplitKeys3(allkeys)
	Pfblue2("r0 = %v\n", r0)
	Pfblue2("r1 = %v\n", r1)
	Pfblue2("r2 = %v\n", r2)
	chk.Strings(tst, "r0", r0, keys)
	chk.Strings(tst, "r1", r1, k1)
	chk.Strings(tst, "r2", r2, k2)

	allkeys = JoinKeys3(keys, []string{}, k2, ",")
	Pfpink("allkeys = %q\n", allkeys)
	chk.String(tst, allkeys, "ux uy uz pl sl,, more1 more2")

	allkeys = JoinKeys3(keys, []string{}, []string{}, ",")
	Pfpink("allkeys = %q\n", allkeys)
	chk.String(tst, allkeys, "ux uy uz pl sl,,")

	allkeys = JoinKeys3([]string{}, k1, k2, ",")
	Pfpink("allkeys = %q\n", allkeys)
	chk.String(tst, allkeys, ", ex1 ex2, more1 more2")

	r0, r1, r2 = SplitKeys3("a ,b c,")
	Pfblue2("r0 = %v\n", r0)
	Pfblue2("r1 = %v\n", r1)
	Pfblue2("r2 = %v\n", r2)
	chk.Strings(tst, "r0", r0, []string{"a"})
	chk.Strings(tst, "r1", r1, []string{"b", "c"})
	chk.Strings(tst, "r2", r2, []string{})
}

func Test_parsing03(tst *testing.T) {

	//verbose()
	chk.PrintTitle("parsing03")

	keys := []string{"ux", "uy", "uz", "pl", "sl"}
	k1, k2, k3 := []string{"ex1", "ex2"}, []string{"more1", "more2"}, []string{"a", "b", "c"}
	allkeys := JoinKeys4(keys, k1, k2, k3, "")
	Pfpink("allkeys = %q\n", allkeys)
	chk.String(tst, allkeys, "ux uy uz pl sl ex1 ex2 more1 more2 a b c")

	allkeys = JoinKeys4(keys, k1, k2, k3, ",")
	Pfpink("allkeys = %q\n", allkeys)
	chk.String(tst, allkeys, "ux uy uz pl sl, ex1 ex2, more1 more2, a b c")

	r0, r1, r2, r3 := SplitKeys4(allkeys)
	Pfblue2("r0 = %v\n", r0)
	Pfblue2("r1 = %v\n", r1)
	Pfblue2("r2 = %v\n", r2)
	Pfblue2("r3 = %v\n", r3)
	chk.Strings(tst, "r0", r0, keys)
	chk.Strings(tst, "r1", r1, k1)
	chk.Strings(tst, "r2", r2, k2)
	chk.Strings(tst, "r3", r3, k3)

	allkeys = JoinKeys4(keys, []string{}, k2, []string{}, ",")
	Pfpink("allkeys = %q\n", allkeys)
	chk.String(tst, allkeys, "ux uy uz pl sl,, more1 more2,")
}

func Test_parsing04(tst *testing.T) {

	//verbose()
	chk.PrintTitle("parsing04")

	keys := []string{"ux", "uy", "uz"}
	skeys := JoinKeysPre("R", keys)
	Pforan("keys  = %+v\n", keys)
	Pforan("skeys = %q\n", skeys)
	chk.String(tst, skeys, "Rux Ruy Ruz")
}

func Test_parsing05(tst *testing.T) {

	//verbose()
	chk.PrintTitle("parsing05. split by spaces quoted")

	str := " arg1 arg2 '   hello    world' '' \"  another hello world   \" "
	res := SplitSpacesQuoted(str)
	Pforan("res = %q\n", res)
	chk.String(tst, res[0], "arg1")
	chk.String(tst, res[1], "arg2")
	chk.String(tst, res[2], "'   hello    world'")
	chk.String(tst, res[3], "''")
	chk.String(tst, res[4], "\"  another hello world   \"")
}

func Test_parsing06(tst *testing.T) {

	//verbose()
	chk.PrintTitle("parsing06. extract within brackets")

	str := "(arg1, (arg2.1, arg2.2),  arg3, arg4, (arg5.1,arg5.2,  arg5.3 ) )"
	res := SplitWithinParentheses(str)
	Pforan("%q\n", str)
	for _, r := range res {
		Pf("%q\n", r)
	}
	chk.String(tst, res[0], "arg1")
	chk.String(tst, res[1], "arg2.1  arg2.2")
	chk.String(tst, res[2], "arg3")
	chk.String(tst, res[3], "arg4")
	chk.String(tst, res[4], "arg5.1 arg5.2   arg5.3 ")
}

func Test_parsing07(tst *testing.T) {

	//verbose()
	chk.PrintTitle("parsing07. float64 fields")

	str := "1.0   1111.11 2.0   3.0"
	res := SplitFloats(str)
	Pforan("floats = %v\n", res)
	chk.Array(tst, "res", 1e-17, res, []float64{1, 1111.11, 2, 3})

	str = "1   1111 2  3"
	ints := SplitInts(str)
	Pforan("ints = %v\n", ints)
	chk.Ints(tst, "res", ints, []int{1, 1111, 2, 3})
}
<commit_msg>Remove term colors in test file<commit_after>// Copyright 2016 The Gosl Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package io

import (
	"testing"

	"github.com/cpmech/gosl/chk"
)

func Test_strpair01(tst *testing.T) {

	//verbose()
	chk.PrintTitle("strpair01")

	str := "keyA: value :"
	key, val := ExtractStrPair(str, ":")
	Pforan("key = %q\n", key)
	Pforan("val = %q\n", val)
	if key != "keyA" {
		tst.Errorf("key %q is incorrect\n", key)
	}
	if val != "value" {
		tst.Errorf("val %q is incorrect\n", val)
	}
}

func Test_keycode01(tst *testing.T) {

	//verbose()
	chk.PrintTitle("keycode01")

	str := "!typeA:keycodeA !typeB:keycodeB!typeC  : keycodeC"
	if resA, ok := Keycode(str, "typeA"); !ok {
		tst.Errorf("cannot find   typeA")
	} else {
		if resA != "keycodeA" {
			tst.Errorf("resA != keycodeA")
		}
	}
	if resB, ok := Keycode(str, "typeB"); !ok {
		tst.Errorf("cannot find   typeB")
	} else {
		if resB != "keycodeB" {
			tst.Errorf("resB != keycodeB")
		}
	}
	if resC, ok := Keycode(str, "typeC"); !ok {
		tst.Errorf("cannot find   typeC")
	} else {
		if resC != "keycodeC" {
			tst.Errorf("resC != keycodeC")
		}
	}
	if resD, ok := Keycode(str, "typeD"); ok {
		tst.Errorf("must not find typeD")
	} else {
		if resD != "" {
			tst.Errorf("resD != \"\"    ")
		}
	}

	res, ok := Keycode("", "")
	if res != "" {
		tst.Errorf("error when handling empty string")
	}
	if ok {
		tst.Errorf("error when handling empty string")
	}

	res, ok = Keycode("!", "")
	if res != "" {
		tst.Errorf("error when handling '!' string")
	}
	if ok {
		tst.Errorf("error when handling '!' string")
	}

	res, ok = Keycode("!keyA !keyB", "keyA")
	if res != "" {
		tst.Errorf("error when handling '!keyA !keyB' string")
	}
	if !ok {
		tst.Errorf("error when handling '!keyA !keyB' string")
	}
}

func Test_keycode02(tst *testing.T) {

	//verbose()
	chk.PrintTitle("keycode02")

	str := "!flagA !typeA:keycodeA !typeB:keycodeB!typeC  : keycodeC !FlagB"
	res := Keycodes(str)
	chk.Strings(tst, "keycodes", res, []string{"flagA", "typeA", "typeB", "typeC", "FlagB"})
}

func Test_parsing02(tst *testing.T) {

	//verbose()
	chk.PrintTitle("parsing02")

	keys := []string{"ux", "uy", "uz", "pl", "sl"}
	skeys := JoinKeys(keys)
	kkeys := SplitKeys(skeys)
	Pforan("keys  = %+v\n", keys)
	Pforan("skeys = %q\n", skeys)
	Pforan("kkeys = %+v\n", kkeys)
	chk.Strings(tst, "keys", keys, kkeys)

	k1, k2 := []string{"ex1", "ex2"}, []string{"more1", "more2"}
	allkeys := JoinKeys3(keys, k1, k2, "")
	Pfpink("allkeys = %q\n", allkeys)
	chk.String(tst, allkeys, "ux uy uz pl sl ex1 ex2 more1 more2")

	allkeys = JoinKeys3(keys, k1, k2, ",")
	Pfpink("allkeys = %q\n", allkeys)
	chk.String(tst, allkeys, "ux uy uz pl sl, ex1 ex2, more1 more2")

	r0, r1, r2 := SplitKeys3(allkeys)
	Pfblue2("r0 = %v\n", r0)
	Pfblue2("r1 = %v\n", r1)
	Pfblue2("r2 = %v\n", r2)
	chk.Strings(tst, "r0", r0, keys)
	chk.Strings(tst, "r1", r1, k1)
	chk.Strings(tst, "r2", r2, k2)

	allkeys = JoinKeys3(keys, []string{}, k2, ",")
	Pfpink("allkeys = %q\n", allkeys)
	chk.String(tst, allkeys, "ux uy uz pl sl,, more1 more2")

	allkeys = JoinKeys3(keys, []string{}, []string{}, ",")
	Pfpink("allkeys = %q\n", allkeys)
	chk.String(tst, allkeys, "ux uy uz pl sl,,")

	allkeys = JoinKeys3([]string{}, k1, k2, ",")
	Pfpink("allkeys = %q\n", allkeys)
	chk.String(tst, allkeys, ", ex1 ex2, more1 more2")

	r0, r1, r2 = SplitKeys3("a ,b c,")
	Pfblue2("r0 = %v\n", r0)
	Pfblue2("r1 = %v\n", r1)
	Pfblue2("r2 = %v\n", r2)
	chk.Strings(tst, "r0", r0, []string{"a"})
	chk.Strings(tst, "r1", r1, []string{"b", "c"})
	chk.Strings(tst, "r2", r2, []string{})
}

func Test_parsing03(tst *testing.T) {

	//verbose()
	chk.PrintTitle("parsing03")

	keys := []string{"ux", "uy", "uz", "pl", "sl"}
	k1, k2, k3 := []string{"ex1", "ex2"}, []string{"more1", "more2"}, []string{"a", "b", "c"}
	allkeys := JoinKeys4(keys, k1, k2, k3, "")
	Pfpink("allkeys = %q\n", allkeys)
	chk.String(tst, allkeys, "ux uy uz pl sl ex1 ex2 more1 more2 a b c")

	allkeys = JoinKeys4(keys, k1, k2, k3, ",")
	Pfpink("allkeys = %q\n", allkeys)
	chk.String(tst, allkeys, "ux uy uz pl sl, ex1 ex2, more1 more2, a b c")

	r0, r1, r2, r3 := SplitKeys4(allkeys)
	Pfblue2("r0 = %v\n", r0)
	Pfblue2("r1 = %v\n", r1)
	Pfblue2("r2 = %v\n", r2)
	Pfblue2("r3 = %v\n", r3)
	chk.Strings(tst, "r0", r0, keys)
	chk.Strings(tst, "r1", r1, k1)
	chk.Strings(tst, "r2", r2, k2)
	chk.Strings(tst, "r3", r3, k3)

	allkeys = JoinKeys4(keys, []string{}, k2, []string{}, ",")
	Pfpink("allkeys = %q\n", allkeys)
	chk.String(tst, allkeys, "ux uy uz pl sl,, more1 more2,")
}

func Test_parsing04(tst *testing.T) {

	//verbose()
	chk.PrintTitle("parsing04")

	keys := []string{"ux", "uy", "uz"}
	skeys := JoinKeysPre("R", keys)
	Pforan("keys  = %+v\n", keys)
	Pforan("skeys = %q\n", skeys)
	chk.String(tst, skeys, "Rux Ruy Ruz")
}

func Test_parsing05(tst *testing.T) {

	//verbose()
	chk.PrintTitle("parsing05. split by spaces quoted")

	str := " arg1 arg2 '   hello    world' '' \"  another hello world   \" "
	res := SplitSpacesQuoted(str)
	Pforan("res = %q\n", res)
	chk.String(tst, res[0], "arg1")
	chk.String(tst, res[1], "arg2")
	chk.String(tst, res[2], "'   hello    world'")
	chk.String(tst, res[3], "''")
	chk.String(tst, res[4], "\"  another hello world   \"")
}

func Test_parsing06(tst *testing.T) {

	//verbose()
	chk.PrintTitle("parsing06. extract within brackets")

	str := "(arg1, (arg2.1, arg2.2),  arg3, arg4, (arg5.1,arg5.2,  arg5.3 ) )"
	res := SplitWithinParentheses(str)
	Pforan("%q\n", str)
	for _, r := range res {
		Pf("%q\n", r)
	}
	chk.String(tst, res[0], "arg1")
	chk.String(tst, res[1], "arg2.1  arg2.2")
	chk.String(tst, res[2], "arg3")
	chk.String(tst, res[3], "arg4")
	chk.String(tst, res[4], "arg5.1 arg5.2   arg5.3 ")
}

func Test_parsing07(tst *testing.T) {

	//verbose()
	chk.PrintTitle("parsing07. float64 fields")

	str := "1.0   1111.11 2.0   3.0"
	res := SplitFloats(str)
	Pforan("floats = %v\n", res)
	chk.Array(tst, "res", 1e-17, res, []float64{1, 1111.11, 2, 3})

	str = "1   1111 2  3"
	ints := SplitInts(str)
	Pforan("ints = %v\n", ints)
	chk.Ints(tst, "res", ints, []int{1, 1111, 2, 3})
}
<|endoftext|>


<commit_before>package view

import (
	//"log"
	"errors"
	"fmt"
	"github.com/ha/doozer"
	"net"
)

func PublishAddr(addr string) error {
	doozerConn, err := doozer.Dial("localhost:8046")
	if err != nil {
		return errors.New(fmt.Sprint("Failed to connect to Doozer: ", err))
	}
	defer doozerConn.Close()

	rev, err := doozerConn.Rev()
	if err != nil {
		return err
	}

	files, err := doozerConn.Getdir("/goft/servers/", rev, 0, -1)
	if err != nil && err.Error() != "NOENT" {
		return err
	}

	id := len(files)
	_, err = doozerConn.Set(fmt.Sprintf("/goft/servers/%d", id), 0, []byte(addr))
	for err != nil && err.Error() == "REV_MISMATCH" {
		id++
		_, err = doozerConn.Set(fmt.Sprintf("/goft/servers/%d", id), 0, []byte(addr))
	}
	if err != nil {
		return err
	}

	return nil
}

func GetRunningServer() (string, error) {
	doozerConn, err := doozer.Dial("localhost:8046")
	if err != nil {
		return "", err
	}
	defer doozerConn.Close()

	rev, err := doozerConn.Rev()
	if err != nil {
		return "", err
	}

	files, err := doozerConn.Getdir("/goft/servers", rev, 0, -1)
	if err != nil {
		return "", err
	}
	//log.Println(files)

	for _, file := range files {
		path := fmt.Sprintf("/goft/servers/%v", file)
		addr, rev, err := doozerConn.Get(path, nil)
		if err != nil {
			return "", err
		}

		//fmt.Println(string(addr))
		conn, err := net.Dial("tcp", string(addr))
		if err != nil {
			//fmt.Println("falhou dial", err)
			err := doozerConn.Del(path, rev)
			if err != nil {
				return "", err
			}
			continue
		}
		defer conn.Close()

		if err != nil {
			return "", err
		}
		//log.Println("Did it!")
		return conn.RemoteAddr().String(), nil
	}
	return "", errors.New("Failed to find a running server")
}
<commit_msg>Remove doozer dependency<commit_after><|endoftext|>


<commit_before>package handlers

import (
	"bytes"
	"errors"
	"fmt"
	"github.com/fatih/color"
	"gopkg.in/urfave/cli.v2"
	"net/http"
	"os"
)

func AppStart(c *cli.Context) error {

	env := NewEnv()

	appInfo := new(AppInfo)
	err := appInfo.Read(env.Log, env.Path, env.Host)
	if err != nil {
		env.Log.Error(err)
		return err
	}

	if appInfo.UUID == "" {
		err = errors.New("App not found")
		env.Log.Error(err)
		return err
	}

	color.Cyan("Starting %s ...", appInfo.Name)

	res, err := http.Post(fmt.Sprintf("%s/app/%s/start", env.HostUrl, appInfo.UUID), "application/json", new(bytes.Buffer))
	if err != nil {
		env.Log.Error(err)
		return err
	}

	if res.StatusCode != 200 {
		err = errors.New("Something went wrong")
		env.Log.Error(err)
		return err
	}

	color.Cyan("Finished!")

	return nil

}

func AppRestart(c *cli.Context) error {

	env := NewEnv()

	appInfo := new(AppInfo)
	err := appInfo.Read(env.Log, env.Path, env.Host)
	if err != nil {
		env.Log.Error(err)
		return err
	}

	if appInfo.UUID == "" {
		err = errors.New("App not found")
		env.Log.Error(err)
		return err
	}

	color.Cyan("Restarting %s ...", appInfo.Name)

	res, err := http.Post(fmt.Sprintf("%s/app/%s/restart", env.HostUrl, appInfo.UUID), "application/json", new(bytes.Buffer))
	if err != nil {
		env.Log.Error(err)
		return err
	}

	if res.StatusCode != 200 {
		err = errors.New("Something went wrong")
		env.Log.Error(err)
		return err
	}

	color.Cyan("Finished!")

	return nil

}

func AppStop(c *cli.Context) error {

	env := NewEnv()

	appInfo := new(AppInfo)
	err := appInfo.Read(env.Log, env.Path, env.Host)
	if err != nil {
		env.Log.Error(err)
		return err
	}

	if appInfo.UUID == "" {
		err = errors.New("App not found")
		env.Log.Error(err)
		return err
	}

	color.Cyan("Stopping %s ...", appInfo.Name)

	res, err := http.Post(fmt.Sprintf("%s/app/%s/stop", env.HostUrl, appInfo.UUID), "application/json", new(bytes.Buffer))
	if err != nil {
		env.Log.Error(err)
		return err
	}

	if res.StatusCode != 200 {
		err = errors.New("Something went wrong")
		env.Log.Error(err)
		return err
	}

	color.Cyan("Finished!")

	return nil

}

func AppRemove(c *cli.Context) error {

	env := NewEnv()

	appInfo := new(AppInfo)
	err := appInfo.Read(env.Log, env.Path, env.Host)
	if err != nil {
		env.Log.Error(err)
		return err
	}

	if appInfo.UUID == "" {
		err = errors.New("App not found")
		env.Log.Error(err)
		return err
	}

	color.Cyan("Removing %s ...", appInfo.Name)

	req, err := http.NewRequest("DELETE", fmt.Sprintf("%s/app/%s", env.HostUrl, appInfo.UUID), new(bytes.Buffer))
	if err != nil {
		env.Log.Error(err)
		return err
	}

	client := new(http.Client)
	res, err := client.Do(req)
	if err != nil {
		env.Log.Error(err)
		return err
	}

	if res.StatusCode != 200 {
		err = errors.New("Something went wrong")
		env.Log.Error(err)
		return err
	}

	os.Remove(fmt.Sprintf("%s/%s_map", env.StoragePath, env.Host))
	os.Remove(fmt.Sprintf("%s/%s.yaml", env.StoragePath, env.Host))

	color.Cyan("Finished!")

	return nil

}
<commit_msg>removed app uuid<commit_after>package handlers

import (
	"bytes"
	"errors"
	"fmt"
	"github.com/fatih/color"
	"gopkg.in/urfave/cli.v2"
	"net/http"
	"os"
)

func AppStart(c *cli.Context) error {

	env := NewEnv()

	appInfo := new(AppInfo)
	err := appInfo.Read(env.Log, env.Path, env.Host)
	if err != nil {
		env.Log.Error(err)
		return err
	}

	if appInfo.Name == "" {
		err = errors.New("App not found")
		env.Log.Error(err)
		return err
	}

	color.Cyan("Starting %s ...", appInfo.Name)

	res, err := http.Post(fmt.Sprintf("%s/app/%s/start", env.HostUrl, appInfo.Name), "application/json", new(bytes.Buffer))
	if err != nil {
		env.Log.Error(err)
		return err
	}

	if res.StatusCode != 200 {
		err = errors.New("Something went wrong")
		env.Log.Error(err)
		return err
	}

	color.Cyan("Finished!")

	return nil

}

func AppRestart(c *cli.Context) error {

	env := NewEnv()

	appInfo := new(AppInfo)
	err := appInfo.Read(env.Log, env.Path, env.Host)
	if err != nil {
		env.Log.Error(err)
		return err
	}

	if appInfo.Name == "" {
		err = errors.New("App not found")
		env.Log.Error(err)
		return err
	}

	color.Cyan("Restarting %s ...", appInfo.Name)

	res, err := http.Post(fmt.Sprintf("%s/app/%s/restart", env.HostUrl, appInfo.Name), "application/json", new(bytes.Buffer))
	if err != nil {
		env.Log.Error(err)
		return err
	}

	if res.StatusCode != 200 {
		err = errors.New("Something went wrong")
		env.Log.Error(err)
		return err
	}

	color.Cyan("Finished!")

	return nil

}

func AppStop(c *cli.Context) error {

	env := NewEnv()

	appInfo := new(AppInfo)
	err := appInfo.Read(env.Log, env.Path, env.Host)
	if err != nil {
		env.Log.Error(err)
		return err
	}

	if appInfo.Name == "" {
		err = errors.New("App not found")
		env.Log.Error(err)
		return err
	}

	color.Cyan("Stopping %s ...", appInfo.Name)

	res, err := http.Post(fmt.Sprintf("%s/app/%s/stop", env.HostUrl, appInfo.Name), "application/json", new(bytes.Buffer))
	if err != nil {
		env.Log.Error(err)
		return err
	}

	if res.StatusCode != 200 {
		err = errors.New("Something went wrong")
		env.Log.Error(err)
		return err
	}

	color.Cyan("Finished!")

	return nil

}

func AppRemove(c *cli.Context) error {

	env := NewEnv()

	appInfo := new(AppInfo)
	err := appInfo.Read(env.Log, env.Path, env.Host)
	if err != nil {
		env.Log.Error(err)
		return err
	}

	if appInfo.Name == "" {
		err = errors.New("App not found")
		env.Log.Error(err)
		return err
	}

	color.Cyan("Removing %s ...", appInfo.Name)

	req, err := http.NewRequest("DELETE", fmt.Sprintf("%s/app/%s", env.HostUrl, appInfo.Name), new(bytes.Buffer))
	if err != nil {
		env.Log.Error(err)
		return err
	}

	client := new(http.Client)
	res, err := client.Do(req)
	if err != nil {
		env.Log.Error(err)
		return err
	}

	if res.StatusCode != 200 {
		err = errors.New("Something went wrong")
		env.Log.Error(err)
		return err
	}

	os.Remove(fmt.Sprintf("%s/%s_map", env.StoragePath, env.Host))
	os.Remove(fmt.Sprintf("%s/%s.yaml", env.StoragePath, env.Host))

	color.Cyan("Finished!")

	return nil

}
<|endoftext|>


<commit_before>package main

/*
 * A harness for running fio on a bunch of devices consistently. I use this
 * to run a series of tests on my test bench. Processes a directory containing
 * fio configs, generating a new directory with filled-out configs for fio and
 * the graphing tools in this package.
 *
 * The JSON device file is an array of devices and some relevant data. e.g.
 * [
 *   {
 *     "device":     "/dev/disk/by-id/ata-Samsung_SSD_840_PRO_Series_S1ATNEAD541857W",
 *     "mountpoint": "/mnt/sda",
 *     "filesystem": "ext4",
 *     "brand":      "Samsung",
 *     "series":     "840 PRO",
 *     "model":      "S1ATNEAD541857W",
 *     "capacity":   256060514304,
 *     "rotational": false,
 *     "transport":  "SATA",
 *     "hba":        "AHCI",
 *     "media":      "MLC",
 *     "blocksize":  512
 *   }
 * ]
 *
 * Descriptions:
 *   device:     always use the /dev/disk/by-id/ path
 *   mountpoint: location where the filesystem is mounted
 *   filesystem: ext4, xfs, zfs, btrfs, ntfs-3g
 *   brand:      Samsung, Fusion IO, I/O Switch Tech, Seagate, Western Digital, etc.
 *   series:     "840 PRO",
 *   model:      `sg_inq /dev/sda`
 *   capacity:   `blockdev --getsize64 /dev/sda`
 *   rotational: false for SSD, true for HDD, true if device contains any HDD
 *   transport:  SATA, SAS, PCIe, MDRAID, iSCSI, virtio
 *   hba:        ioMemory, AHCI, SAS3004, USB3, mixed (for MDRAID)
 *   media:      MLC, Iron (for HDDs), TLC, SLC, Hybrid (SSHD)
 *   blocksize:  `blockdev --getpbsz /dev/sda`
 */

import (
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path"
	"path/filepath"
)

// loaded from JSON per the -devs flag or defaults to <hostname>.json
type Device struct {
	Device     string
	Mountpoint string
	Filesystem string
	Brand      string
	Series     string
	Model      string
	Capacity   int64
	Rotational bool
	Transport  string
	HBA        string
	Media      string
	BlockSize  int
}

type DeviceList []Device

// fio has some built-in variable expansion but I want to save the artifacts
// to a git repo
type FioTmpl struct {
	Filename string // full path to the file
	Basename string // used to generate output filenames
	Config   string // raw template data
}

type FioTmplList []FioTmpl

// test data generated on the fly based on info above
type Test struct {
	Name        string
	Description string
	BWLog       string
	LatLog      string
	IopsLog     string
}

// command-line flags (global)
var devJsonFlag, confDirFlag string

func init() {
	// the default device filename is <hostname>.json
	devfile, err := os.Hostname()
	if err != nil {
		devfile = "devices"
	}
	devfile = fmt.Sprintf("%s.json", devfile)

	flag.StringVar(&devJsonFlag, "devs", devfile, "A JSON file containing device metadata")
	flag.StringVar(&confDirFlag, "conf", "./fio_configs", "A directroy containing fio config templates")
}

func main() {
	flag.Parse()

	// load the device information from JSON
	var devs DeviceList
	mdbuf, err := ioutil.ReadFile(devJsonFlag)
	if err != nil {
		log.Fatalf("Could not read '%s': %s\n", devJsonFlag, err)
	}
	err = json.Unmarshal(mdbuf, &devs)
	if err != nil {
		log.Fatalf("Could not parse JSON: %s\n", err)
	}

	// load the template files into memory
	tmpls := loadFioTmpl(confDirFlag)
	for _, tp := range tmpls {
		// TODO: fill this in
		fmt.Printf("TP: %v\n", tp)
	}

	// TODO:
	// for each device/fio config combination, create a config file in
	// a new directory named <test>-<iso8601 date> with one directory
	// per test so fio can be excuted in those directories, keeping
	// data generated along side the configs
	for _, dev := range devs {
		fmt.Printf("Dev: %v\n", dev)
	}
}

func loadFioTmpl(dir string) FioTmplList {
	out := make(FioTmplList, 1)

	visitor := func(fpath string, f os.FileInfo, err error) error {
		if err != nil {
			log.Fatalf("Encountered an error while loading fio config '%s': %s", fpath, err)
		}

		fname := path.Base(fpath)
		ext := path.Ext(fname)
		if ext == ".fio" {
			data, err := ioutil.ReadFile(fpath)
			if err != nil {
				log.Fatalf("Could not read fio config '%s': %s", fpath, err)
			}

			out = append(out, FioTmpl{fpath, fname, string(data)})
		}

		return nil
	}

	err := filepath.Walk(dir, visitor)
	if err != nil {
		log.Fatalf("Could not load configs in '%s': %s", dir, err)
	}

	return out
}
<commit_msg>Move json loading into a function.<commit_after>package main

/*
 * A harness for running fio on a bunch of devices consistently. I use this
 * to run a series of tests on my test bench. Processes a directory containing
 * fio configs, generating a new directory with filled-out configs for fio and
 * the graphing tools in this package.
 *
 * The JSON device file is an array of devices and some relevant data. e.g.
 * [
 *   {
 *     "device":     "/dev/disk/by-id/ata-Samsung_SSD_840_PRO_Series_S1ATNEAD541857W",
 *     "mountpoint": "/mnt/sda",
 *     "filesystem": "ext4",
 *     "brand":      "Samsung",
 *     "series":     "840 PRO",
 *     "model":      "S1ATNEAD541857W",
 *     "capacity":   256060514304,
 *     "rotational": false,
 *     "transport":  "SATA",
 *     "hba":        "AHCI",
 *     "media":      "MLC",
 *     "blocksize":  512
 *   }
 * ]
 *
 * Descriptions:
 *   device:     always use the /dev/disk/by-id/ path
 *   mountpoint: location where the filesystem is mounted
 *   filesystem: ext4, xfs, zfs, btrfs, ntfs-3g
 *   brand:      Samsung, Fusion IO, I/O Switch Tech, Seagate, Western Digital, etc.
 *   series:     "840 PRO",
 *   model:      `sg_inq /dev/sda`
 *   capacity:   `blockdev --getsize64 /dev/sda`
 *   rotational: false for SSD, true for HDD, true if device contains any HDD
 *   transport:  SATA, SAS, PCIe, MDRAID, iSCSI, virtio
 *   hba:        ioMemory, AHCI, SAS3004, USB3, mixed (for MDRAID)
 *   media:      MLC, Iron (for HDDs), TLC, SLC, Hybrid (SSHD)
 *   blocksize:  `blockdev --getpbsz /dev/sda`
 */

import (
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path"
	"path/filepath"
)

// loaded from JSON per the -devs flag or defaults to <hostname>.json
type Device struct {
	Device     string
	Mountpoint string
	Filesystem string
	Brand      string
	Series     string
	Model      string
	Capacity   int64
	Rotational bool
	Transport  string
	HBA        string
	Media      string
	BlockSize  int
}

type DeviceList []Device

// fio has some built-in variable expansion but I want to save the artifacts
// to a git repo
type FioTmpl struct {
	Filename string // full path to the file
	Basename string // used to generate output filenames
	Config   string // raw template data
}

type FioTmplList []FioTmpl

// test data generated on the fly based on info above
type Test struct {
	Name        string
	Description string
	BWLog       string
	LatLog      string
	IopsLog     string
}

// command-line flags (global)
var devJsonFlag, confDirFlag string

func init() {
	// the default device filename is <hostname>.json
	devfile, err := os.Hostname()
	if err != nil {
		devfile = "devices"
	}
	devfile = fmt.Sprintf("%s.json", devfile)

	flag.StringVar(&devJsonFlag, "devs", devfile, "A JSON file containing device metadata")
	flag.StringVar(&confDirFlag, "conf", "./fio_configs", "A directroy containing fio config templates")
}

func main() {
	flag.Parse()

	// load device data from json
	devs := loadDevJson(devJsonFlag)

	// load the fio config templates into memory
	tmpls := loadFioTmpl(confDirFlag)

	for _, tp := range tmpls {
		// TODO: fill this in
		fmt.Printf("TP: %v\n", tp)
	}

	// TODO:
	// for each device/fio config combination, create a config file in
	// a new directory named <test>-<iso8601 date> with one directory
	// per test so fio can be excuted in those directories, keeping
	// data generated along side the configs
	for _, dev := range devs {
		fmt.Printf("Dev: %v\n", dev)
	}
}

func loadDevJson(fname string) (devs DeviceList) {
	// load the device information from JSON
	mdbuf, err := ioutil.ReadFile(devJsonFlag)
	if err != nil {
		log.Fatalf("Could not read '%s': %s\n", fname, err)
	}
	err = json.Unmarshal(mdbuf, &devs)
	if err != nil {
		log.Fatalf("Could not parse JSON: %s\n", err)
	}

	return devs
}

func loadFioTmpl(dir string) FioTmplList {
	out := make(FioTmplList, 1)

	visitor := func(fpath string, f os.FileInfo, err error) error {
		if err != nil {
			log.Fatalf("Encountered an error while loading fio config '%s': %s", fpath, err)
		}

		fname := path.Base(fpath)
		ext := path.Ext(fname)
		if ext == ".fio" {
			data, err := ioutil.ReadFile(fpath)
			if err != nil {
				log.Fatalf("Could not read fio config '%s': %s", fpath, err)
			}

			out = append(out, FioTmpl{fpath, fname, string(data)})
		}

		return nil
	}

	err := filepath.Walk(dir, visitor)
	if err != nil {
		log.Fatalf("Could not load configs in '%s': %s", dir, err)
	}

	return out
}
<|endoftext|>


<commit_before>package lexer

import (
	"io"
	"io/ioutil"
	"jacob/black/token"
)

// Lexer performs the tokenisation on a io.Reader
type Lexer struct {
	buff []byte

	curr int
	next int

	pos token.Position

	char byte

	last token.Type
}

// WithReader creates a new Lexer from the reader
func WithReader(reader io.Reader, filename string) *Lexer {
	b, err := ioutil.ReadAll(reader)
	if err != nil {
		panic(err)
	}

	l := &Lexer{buff: b}
	l.init(filename)

	return l
}

// WithString creates a new Lexer from a string
func WithString(str, filename string) *Lexer {
	l := &Lexer{buff: []byte(str)}
	l.init(filename)
	return l
}

// init sets the initial positons for the lexer
func (l *Lexer) init(filename string) {
	l.pos.Filename = filename
	l.pos.Line = 1

	l.nextChar()
}

// Next returns the next token in the input stream
func (l *Lexer) Next() token.Token {
	var tok token.Token

	l.consumeWhitespace()

	switch l.char {
	case '=':
		if l.peekChar() == '=' {
			char := l.char
			l.nextChar()

			b := make([]byte, 2)
			b[0] = char
			b[1] = l.char

			tok = token.Token{Type: token.Equal, Literal: string(b)}
		} else {
			tok = token.New(token.Assign, l.char, l.pos)
		}
	case '+':
		tok = token.New(token.Plus, l.char, l.pos)
	case '-':
		tok = token.New(token.Minus, l.char, l.pos)
	case '*':
		tok = token.New(token.Times, l.char, l.pos)
	case '/':
		tok = token.New(token.Divide, l.char, l.pos)
	case '!':
		if l.peekChar() == '=' {
			char := l.char
			l.nextChar()

			b := make([]byte, 2)
			b[0] = char
			b[1] = l.char

			tok = token.Token{Type: token.NotEqual, Literal: string(b)}
		} else {
			tok = token.New(token.Bang, l.char, l.pos)
		}
	case '<':
		tok = token.New(token.Less, l.char, l.pos)
	case '>':
		tok = token.New(token.Greater, l.char, l.pos)
	case '{':
		tok = token.New(token.LBrace, l.char, l.pos)
	case '}':
		tok = token.New(token.RBrace, l.char, l.pos)
	case '(':
		tok = token.New(token.LParen, l.char, l.pos)
	case ')':
		tok = token.New(token.RParen, l.char, l.pos)
	case '[':
		tok = token.New(token.LBracket, l.char, l.pos)
	case ']':
		tok = token.New(token.RBracket, l.char, l.pos)
	case '|':
		tok = token.New(token.Bar, l.char, l.pos)
	case ',':
		tok = token.New(token.Comma, l.char, l.pos)
	case ';':
		tok = token.New(token.Terminator, l.char, l.pos)
	case 0:
		tok = token.New(token.EOF, l.char, l.pos)
	default:
		if isLetter(l.char) {
			return l.readIdentifier()
		} else if isDigit(l.char) {
			return l.readNumber()
		} else {
			tok = token.New(token.Illegal, l.char, l.pos)
		}
	}

	l.nextChar()

	l.last = tok.Type
	return tok
}

func isLetter(c byte) bool {
	return 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || c == '_'
}

func isDigit(c byte) bool {
	return '0' <= c && c <= '9'
}

func (l *Lexer) consumeWhitespace() {
	for l.char == ' ' || l.char == '\t' || l.char == '\n' || l.char == '\r' {
		// semi-colon insertion
		// only add eof of statment semi-colon if
		if l.char == '\n' && l.last != token.LBrace && l.last != token.Terminator {
			l.char = ';'
			return
		}
		l.nextChar()
	}
}

func (l *Lexer) readIdentifier() token.Token {
	pos := l.pos

	p := l.curr
	for isLetter(l.char) {
		l.nextChar()
	}
	id := string(l.buff[p:l.curr])

	l.last = token.Identifier
	return token.Token{token.LookupIdenifier(id), id, pos}
}

// TODO: make parse double and int
func (l *Lexer) readNumber() token.Token {
	pos := l.pos

	p := l.curr
	for isDigit(l.char) {
		l.nextChar()
	}

	l.last = token.Int
	return token.Token{token.Int, string(l.buff[p:l.curr]), pos}
}

func (l *Lexer) nextChar() {
	if l.next >= len(l.buff) {
		l.char = 0
	} else {
		l.char = l.buff[l.next]
	}

	l.curr = l.next
	l.next++

	// update position data
	l.pos.Col++
	l.pos.Offset = l.curr
	if l.char == '\n' {
		l.pos.Line++
		l.pos.Col = 0
	}

}

func (l *Lexer) peekChar() byte {
	if l.next >= len(l.buff) {
		return 0
	}
	return l.buff[l.next]
}
<commit_msg>comments<commit_after>package lexer

import (
	"io"
	"io/ioutil"
	"jacob/black/token"
)

// Lexer performs the tokenisation on a io.Reader
type Lexer struct {
	// src input buffer
	buff []byte

	// current index in buffer
	// is used to state the current place
	curr int
	// next index in buffer
	// is used to read the next character without affecting curr
	next int

	// position in the src visually
	pos token.Position

	// the current character the lexer is looking at
	char byte

	// the last token lexed
	// used for inserting semi-colon on line break
	last token.Type
}

// WithReader creates a new Lexer from the reader
func WithReader(reader io.Reader, filename string) *Lexer {
	b, err := ioutil.ReadAll(reader)
	if err != nil {
		panic(err)
	}

	l := &Lexer{buff: b}
	l.init(filename)

	return l
}

// WithString creates a new Lexer from a string
func WithString(str, filename string) *Lexer {
	l := &Lexer{buff: []byte(str)}
	l.init(filename)
	return l
}

// init sets the initial positons for the lexer
func (l *Lexer) init(filename string) {
	l.pos.Filename = filename
	l.pos.Line = 1

	l.nextChar()
}

// Next returns the next token in the input stream
func (l *Lexer) Next() token.Token {
	var tok token.Token

	l.consumeWhitespace()

	switch l.char {
	case '=':
		if l.peekChar() == '=' {
			char := l.char
			l.nextChar()

			b := make([]byte, 2)
			b[0] = char
			b[1] = l.char

			tok = token.Token{Type: token.Equal, Literal: string(b)}
		} else {
			tok = token.New(token.Assign, l.char, l.pos)
		}
	case '+':
		tok = token.New(token.Plus, l.char, l.pos)
	case '-':
		tok = token.New(token.Minus, l.char, l.pos)
	case '*':
		tok = token.New(token.Times, l.char, l.pos)
	case '/':
		tok = token.New(token.Divide, l.char, l.pos)
	case '!':
		if l.peekChar() == '=' {
			char := l.char
			l.nextChar()

			b := make([]byte, 2)
			b[0] = char
			b[1] = l.char

			tok = token.Token{Type: token.NotEqual, Literal: string(b)}
		} else {
			tok = token.New(token.Bang, l.char, l.pos)
		}
	case '<':
		tok = token.New(token.Less, l.char, l.pos)
	case '>':
		tok = token.New(token.Greater, l.char, l.pos)
	case '{':
		tok = token.New(token.LBrace, l.char, l.pos)
	case '}':
		tok = token.New(token.RBrace, l.char, l.pos)
	case '(':
		tok = token.New(token.LParen, l.char, l.pos)
	case ')':
		tok = token.New(token.RParen, l.char, l.pos)
	case '[':
		tok = token.New(token.LBracket, l.char, l.pos)
	case ']':
		tok = token.New(token.RBracket, l.char, l.pos)
	case '|':
		tok = token.New(token.Bar, l.char, l.pos)
	case ',':
		tok = token.New(token.Comma, l.char, l.pos)
	case ';':
		tok = token.New(token.Terminator, l.char, l.pos)
	case 0:
		tok = token.New(token.EOF, l.char, l.pos)
	default:
		if isLetter(l.char) {
			return l.readIdentifier()
		} else if isDigit(l.char) {
			return l.readNumber()
		} else {
			tok = token.New(token.Illegal, l.char, l.pos)
		}
	}

	l.nextChar()

	l.last = tok.Type
	return tok
}

func isLetter(c byte) bool {
	return 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || c == '_'
}

func isDigit(c byte) bool {
	return '0' <= c && c <= '9'
}

func (l *Lexer) consumeWhitespace() {
	for l.char == ' ' || l.char == '\t' || l.char == '\n' || l.char == '\r' {
		// semi-colon insertion
		// only add eof of statment semi-colon if
		if l.char == '\n' && l.last != token.LBrace && l.last != token.Terminator {
			l.char = ';'
			return
		}
		l.nextChar()
	}
}

func (l *Lexer) readIdentifier() token.Token {
	pos := l.pos

	p := l.curr
	for isLetter(l.char) {
		l.nextChar()
	}
	id := string(l.buff[p:l.curr])

	l.last = token.Identifier
	return token.Token{token.LookupIdenifier(id), id, pos}
}

// TODO: make parse double and int
func (l *Lexer) readNumber() token.Token {
	pos := l.pos

	p := l.curr
	for isDigit(l.char) {
		l.nextChar()
	}

	l.last = token.Int
	return token.Token{token.Int, string(l.buff[p:l.curr]), pos}
}

func (l *Lexer) nextChar() {
	if l.next >= len(l.buff) {
		l.char = 0
	} else {
		l.char = l.buff[l.next]
	}

	l.curr = l.next
	l.next++

	// update position data
	l.pos.Col++
	l.pos.Offset = l.curr
	if l.char == '\n' {
		l.pos.Line++
		l.pos.Col = 0
	}

}

func (l *Lexer) peekChar() byte {
	if l.next >= len(l.buff) {
		return 0
	}
	return l.buff[l.next]
}
<|endoftext|>


<commit_before>package client

import (
	"encoding/json"
	"net/url"
	"time"

	"golang.org/x/net/context"

	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/events"
	"github.com/docker/docker/api/types/filters"
	timetypes "github.com/docker/docker/api/types/time"
)

// Events returns a stream of events in the daemon. It's up to the caller to close the stream
// by cancelling the context. Once the stream has been completely read an io.EOF error will
// be sent over the error channel. If an error is sent all processing will be stopped. It's up
// to the caller to reopen the stream in the event of an error by reinvoking this method.
func (cli *Client) Events(ctx context.Context, options types.EventsOptions) (<-chan events.Message, <-chan error) {

	messages := make(chan events.Message)
	errs := make(chan error, 1)

	go func() {
		defer close(errs)

		query, err := buildEventsQueryParams(cli.version, options)
		if err != nil {
			errs <- err
			return
		}

		resp, err := cli.get(ctx, "/events", query, nil)
		if err != nil {
			errs <- err
			return
		}
		defer resp.body.Close()

		decoder := json.NewDecoder(resp.body)

		for {
			select {
			case <-ctx.Done():
				errs <- ctx.Err()
				return
			default:
				var event events.Message
				if err := decoder.Decode(&event); err != nil {
					errs <- err
					return
				}

				select {
				case messages <- event:
				case <-ctx.Done():
					errs <- ctx.Err()
					return
				}
			}
		}
	}()

	return messages, errs
}

func buildEventsQueryParams(cliVersion string, options types.EventsOptions) (url.Values, error) {
	query := url.Values{}
	ref := time.Now()

	if options.Since != "" {
		ts, err := timetypes.GetTimestamp(options.Since, ref)
		if err != nil {
			return nil, err
		}
		query.Set("since", ts)
	}

	if options.Until != "" {
		ts, err := timetypes.GetTimestamp(options.Until, ref)
		if err != nil {
			return nil, err
		}
		query.Set("until", ts)
	}

	if options.Filters.Len() > 0 {
		filterJSON, err := filters.ToParamWithVersion(cliVersion, options.Filters)
		if err != nil {
			return nil, err
		}
		query.Set("filters", filterJSON)
	}

	return query, nil
}
<commit_msg>Fixes a race condition in client events monitoring<commit_after>package client

import (
	"encoding/json"
	"net/url"
	"time"

	"golang.org/x/net/context"

	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/events"
	"github.com/docker/docker/api/types/filters"
	timetypes "github.com/docker/docker/api/types/time"
)

// Events returns a stream of events in the daemon. It's up to the caller to close the stream
// by cancelling the context. Once the stream has been completely read an io.EOF error will
// be sent over the error channel. If an error is sent all processing will be stopped. It's up
// to the caller to reopen the stream in the event of an error by reinvoking this method.
func (cli *Client) Events(ctx context.Context, options types.EventsOptions) (<-chan events.Message, <-chan error) {

	messages := make(chan events.Message)
	errs := make(chan error, 1)

	started := make(chan struct{})
	go func() {
		defer close(errs)

		query, err := buildEventsQueryParams(cli.version, options)
		if err != nil {
			close(started)
			errs <- err
			return
		}

		resp, err := cli.get(ctx, "/events", query, nil)
		if err != nil {
			close(started)
			errs <- err
			return
		}
		defer resp.body.Close()

		decoder := json.NewDecoder(resp.body)

		close(started)
		for {
			select {
			case <-ctx.Done():
				errs <- ctx.Err()
				return
			default:
				var event events.Message
				if err := decoder.Decode(&event); err != nil {
					errs <- err
					return
				}

				select {
				case messages <- event:
				case <-ctx.Done():
					errs <- ctx.Err()
					return
				}
			}
		}
	}()
	<-started

	return messages, errs
}

func buildEventsQueryParams(cliVersion string, options types.EventsOptions) (url.Values, error) {
	query := url.Values{}
	ref := time.Now()

	if options.Since != "" {
		ts, err := timetypes.GetTimestamp(options.Since, ref)
		if err != nil {
			return nil, err
		}
		query.Set("since", ts)
	}

	if options.Until != "" {
		ts, err := timetypes.GetTimestamp(options.Until, ref)
		if err != nil {
			return nil, err
		}
		query.Set("until", ts)
	}

	if options.Filters.Len() > 0 {
		filterJSON, err := filters.ToParamWithVersion(cliVersion, options.Filters)
		if err != nil {
			return nil, err
		}
		query.Set("filters", filterJSON)
	}

	return query, nil
}
<|endoftext|>


<commit_before>package veneur

import (
	"net/http"
	"testing"

	"github.com/DataDog/datadog-go/statsd"
	"github.com/stretchr/testify/assert"
)

func TestNewDatadogSpanSinkConfig(t *testing.T) {
	// test the variables that have been renamed
	config := Config{
		DatadogTraceAPIAddress: "http://trace",
	}
	stats, _ := statsd.NewBuffered(config.StatsAddress, 1024)
	ddSink, err := NewDatadogSpanSink(&config, stats, &http.Client{}, nil)
	if err != nil {
		t.Fatal(err)
	}
	err = ddSink.Start(nil)
	if err != nil {
		t.Fatal(err)
	}

	assert.Equal(t, "http://trace", ddSink.traceAddress)
}
<commit_msg>Add a test that the span sink actually ingests spans<commit_after>package veneur

import (
	"net/http"
	"sync"
	"testing"
	"time"

	"github.com/DataDog/datadog-go/statsd"
	opentracing "github.com/opentracing/opentracing-go"
	otlog "github.com/opentracing/opentracing-go/log"
	"github.com/stretchr/testify/assert"
	"github.com/stripe/veneur/ssf"
)

func TestNewDatadogSpanSinkConfig(t *testing.T) {
	// test the variables that have been renamed
	config := Config{
		DatadogTraceAPIAddress: "http://trace",
	}
	stats, _ := statsd.NewBuffered(config.StatsAddress, 1024)
	ddSink, err := NewDatadogSpanSink(&config, stats, &http.Client{}, nil)
	if err != nil {
		t.Fatal(err)
	}
	err = ddSink.Start(nil)
	if err != nil {
		t.Fatal(err)
	}

	assert.Equal(t, "http://trace", ddSink.traceAddress)
}

type testLSTracer struct {
	finishedSpans []*testLSSpan
}

func (ft *testLSTracer) StartSpan(operationName string, opts ...opentracing.StartSpanOption) opentracing.Span {
	return &testLSSpan{
		name:   operationName,
		opts:   opts,
		client: ft,
	}
}

func (ft *testLSTracer) Inject(sm opentracing.SpanContext, format interface{}, carrier interface{}) error {
	panic("not implemented")
}

func (ft *testLSTracer) Extract(format interface{}, carrier interface{}) (opentracing.SpanContext, error) {
	panic("not implemented")
}

var _ opentracing.Tracer = &testLSTracer{}

type testLSSpan struct {
	name   string
	tags   map[string]interface{}
	opts   []opentracing.StartSpanOption
	client *testLSTracer
}

func (tls *testLSSpan) Finish() {
	tls.FinishWithOptions(opentracing.FinishOptions{})
}

func (tls *testLSSpan) FinishWithOptions(opts opentracing.FinishOptions) {
	tls.client.finishedSpans = append(tls.client.finishedSpans, tls)
}

func (tls *testLSSpan) Context() opentracing.SpanContext {
	return nil
}

func (tls *testLSSpan) SetOperationName(operationName string) opentracing.Span {
	tls.name = operationName
	return tls
}

func (tls *testLSSpan) SetTag(key string, value interface{}) opentracing.Span {
	if tls.tags == nil {
		tls.tags = make(map[string]interface{})
	}
	tls.tags[key] = value
	return tls
}

func (tls *testLSSpan) LogFields(fields ...otlog.Field) {
	panic("not implemented")
}

func (tls *testLSSpan) LogKV(alternatingKeyValues ...interface{}) {
	panic("not implemented")
}

func (tls *testLSSpan) SetBaggageItem(restrictedKey string, value string) opentracing.Span {
	panic("not implemented")
}

func (tls *testLSSpan) BaggageItem(restrictedKey string) string {
	panic("not implemented")
}

func (tls *testLSSpan) Tracer() opentracing.Tracer {
	return tls.client
}

func (tls *testLSSpan) LogEvent(event string) {
	panic("not implemented")
}

func (tls *testLSSpan) LogEventWithPayload(event string, payload interface{}) {
	panic("not implemented")
}

func (tls *testLSSpan) Log(data opentracing.LogData) {
	panic("not implemented")
}

func TestLSSpanSinkIngest(t *testing.T) {
	tracer := &testLSTracer{}
	ls := &lightStepSpanSink{
		tracers:      []opentracing.Tracer{tracer},
		commonTags:   map[string]string{"foo": "bar"},
		serviceCount: make(map[string]int64),
		mutex:        &sync.Mutex{},
	}
	start := time.Now()
	end := start.Add(2 * time.Second)

	testSpan := ssf.SSFSpan{
		TraceId:        1,
		ParentId:       1,
		Id:             2,
		StartTimestamp: int64(start.UnixNano()),
		EndTimestamp:   int64(end.UnixNano()),
		Error:          false,
		Service:        "farts-srv",
		Tags: map[string]string{
			"baz": "qux",
		},
		Indicator: false,
		Name:      "farting farty farts",
	}
	err := ls.Ingest(testSpan)
	assert.NoError(t, err)

	if assert.Equal(t, 1, len(tracer.finishedSpans)) {
		count, ok := ls.serviceCount["farts-srv"]
		assert.True(t, ok, "should have counted")
		assert.EqualValues(t, 1, count)

		span := tracer.finishedSpans[0]
		assert.Equal(t, "farting farty farts", span.name)
		assert.Contains(t, span.tags, "baz")
		assert.Contains(t, span.tags, "foo")
	}
}
<|endoftext|>


<commit_before>// Copyright 2016 CoreOS, Inc.
//
// 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 functional

import (
	"strings"
	"testing"

	"github.com/coreos/fleet/functional/platform"
)

// TestReconfigureServer checks whether fleetd managed to keep its listeners
// across reconfiguration of fleetd after receiving SIGHUP.
func TestReconfigureServer(t *testing.T) {
	cluster, err := platform.NewNspawnCluster("smoke")
	if err != nil {
		t.Fatal(err)
	}
	defer cluster.Destroy()

	m0, err := cluster.CreateMember()
	if err != nil {
		t.Fatal(err)
	}
	_, err = cluster.WaitForNMachines(m0, 1)
	if err != nil {
		t.Fatal(err)
	}

	// Check expected state before reconfiguring fleetd
	stdout, _ := cluster.MemberCommand(m0, "systemctl", "show", "--property=ActiveState", "fleet")
	if strings.TrimSpace(stdout) != "ActiveState=active" {
		t.Fatalf("Fleet unit not reported as active: %s", stdout)
	}
	stdout, _ = cluster.MemberCommand(m0, "systemctl", "show", "--property=Result", "fleet")
	if strings.TrimSpace(stdout) != "Result=success" {
		t.Fatalf("Result for fleet unit not reported as success: %s", stdout)
	}

	// NOTE: we need to sleep once here to get reliable results.
	// Without this sleep, the entire fleetd test always ends up succeeding
	// no matter whether SIGHUP came or not.
	_, _ = cluster.MemberCommand(m0, "sh", "-c", `'sleep 2'`)

	stdout, _ = cluster.MemberCommand(m0, "sudo", "kill", "-HUP", "$(pidof fleetd)")
	if strings.TrimSpace(stdout) != "" {
		t.Fatalf("Sending SIGHUP to fleetd returned: %s", stdout)
	}

	// Check for HTTP listener error looking into the fleetd journal
	stdout, _ = cluster.MemberCommand(m0, "sh", "-c",
		`'journalctl -u fleet | grep "Failed serving HTTP on listener:"'`)
	if strings.TrimSpace(stdout) != "" {
		t.Fatalf("Fleetd log returned error on HTTP listeners: %s", stdout)
	}

	// Check expected state after reconfiguring fleetd
	stdout, _ = cluster.MemberCommand(m0, "systemctl", "show", "--property=ActiveState", "fleet")
	if strings.TrimSpace(stdout) != "ActiveState=active" {
		t.Fatalf("Fleet unit not reported as active: %s", stdout)
	}
	stdout, _ = cluster.MemberCommand(m0, "systemctl", "show", "--property=Result", "fleet")
	if strings.TrimSpace(stdout) != "Result=success" {
		t.Fatalf("Result for fleet unit not reported as success: %s", stdout)
	}
}
<commit_msg>functional: improve TestReconfigureServer() for reliable tests<commit_after>// Copyright 2016 CoreOS, Inc.
//
// 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 functional

import (
	"fmt"
	"strings"
	"testing"

	"github.com/coreos/fleet/functional/platform"
	"github.com/coreos/fleet/functional/util"
)

// TestReconfigureServer checks whether fleetd managed to keep its listeners
// across reconfiguration of fleetd after receiving SIGHUP.
func TestReconfigureServer(t *testing.T) {
	cluster, err := platform.NewNspawnCluster("smoke")
	if err != nil {
		t.Fatal(err)
	}
	defer cluster.Destroy()

	m0, err := cluster.CreateMember()
	if err != nil {
		t.Fatal(err)
	}
	_, err = cluster.WaitForNMachines(m0, 1)
	if err != nil {
		t.Fatal(err)
	}

	// NOTE: we need to sleep once here to get reliable results.
	// Without this sleep, the entire fleetd test always ends up succeeding
	// no matter whether SIGHUP came or not.
	_, _ = cluster.MemberCommand(m0, "sh", "-c", `'sleep 2'`)

	err = waitForFleetdSocket(cluster, m0)
	if err != nil {
		t.Fatalf("Failed to get a list of fleetd sockets: %v", err)
	}

	// send a SIGHUP to fleetd, and periodically checks if a message
	// "Reloading configuration" appears in fleet's journal, up to timeout (15) seconds.
	stdout, _ := cluster.MemberCommand(m0, "sudo", "systemctl", "kill", "-s", "SIGHUP", "fleet")
	if strings.TrimSpace(stdout) != "" {
		t.Fatalf("Sending SIGHUP to fleetd returned: %s", stdout)
	}

	err = waitForReloadConfig(cluster, m0)
	if err != nil {
		t.Fatalf("Failed to get log about reconfiguration: %v", err)
	}

	// check if fleetd is still running correctly, by running fleetctl status
	_, _, err = cluster.Fleetctl(m0, "list-units")
	if err != nil {
		t.Fatalf("Unable to check list-units. Please check for fleetd socket. err:%v", err)
	}

	// Check for HTTP listener error looking into the fleetd journal
	stdout, _ = cluster.MemberCommand(m0, "journalctl _PID=$(pidof fleetd)")
	if strings.Contains(strings.TrimSpace(stdout), "Failed serving HTTP on listener:") {
		t.Fatalf("Fleetd log returned error on HTTP listeners: %s", stdout)
	}

	// Check expected state after reconfiguring fleetd
	stdout, _ = cluster.MemberCommand(m0, "systemctl", "show", "--property=ActiveState", "fleet")
	if strings.TrimSpace(stdout) != "ActiveState=active" {
		t.Fatalf("Fleet unit not reported as active: %s", stdout)
	}
	stdout, _ = cluster.MemberCommand(m0, "systemctl", "show", "--property=Result", "fleet")
	if strings.TrimSpace(stdout) != "Result=success" {
		t.Fatalf("Result for fleet unit not reported as success: %s", stdout)
	}
}

// waitForReloadConfig returns if a message "Reloading configuration" exists
// in the journal, periodically checking for the journal up to the timeout.
func waitForReloadConfig(cluster platform.Cluster, m0 platform.Member) (err error) {
	_, err = util.WaitForState(
		func() bool {
			// NOTE: journalctl should run just simply like "journalctl -u fleet",
			// without being piped with grep. Doing
			// "journalctl -u fleet | grep \"Reloading configuration\"" is racy
			// in a subtle way, so that it sometimes fails only on semaphoreci.
			// - dpark 20160408
			stdout, _ := cluster.MemberCommand(m0, "journalctl _PID=$(pidof fleetd)")
			journalfleet := strings.TrimSpace(stdout)
			if !strings.Contains(journalfleet, "Reloading configuration") {
				fmt.Errorf("Fleetd is not fully reconfigured, retrying... entire fleet journal:\n%v", journalfleet)
				return false
			}
			return true
		},
	)
	if err != nil {
		return fmt.Errorf("Reloading configuration log not found: %v", err)
	}

	return nil
}

// waitForFleetdSocket returns if /var/run/fleet.sock exists, periodically
// checking for states.
func waitForFleetdSocket(cluster platform.Cluster, m0 platform.Member) (err error) {
	_, err = util.WaitForState(
		func() bool {
			stdout, _ := cluster.MemberCommand(m0, "test -S /var/run/fleet.sock && echo 1")
			if strings.TrimSpace(stdout) == "" {
				fmt.Errorf("Fleetd is not fully started, retrying...")
				return false
			}
			return true
		},
	)
	if err != nil {
		return fmt.Errorf("Fleetd socket not found: %v", err)
	}

	return nil
}
<|endoftext|>


<commit_before>// +build fvtests

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

package fv_test

import (
	"strconv"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"

	"github.com/projectcalico/felix/fv/containers"
	"github.com/projectcalico/felix/fv/infrastructure"
	"github.com/projectcalico/felix/fv/utils"
	"github.com/projectcalico/felix/fv/workload"
	api "github.com/projectcalico/libcalico-go/lib/apis/v3"
	client "github.com/projectcalico/libcalico-go/lib/clientv3"
)

var _ = Context("_INGRESS-EGRESS_ _BPF-SAFE_ with initialized Felix, etcd datastore, 3 workloads", func() {

	var (
		etcd   *containers.Container
		felix  *infrastructure.Felix
		client client.Interface
		w      [3]*workload.Workload
		cc     *workload.ConnectivityChecker
	)

	BeforeEach(func() {
		felix, etcd, client = infrastructure.StartSingleNodeEtcdTopology(infrastructure.DefaultTopologyOptions())

		// Install a default profile that allows workloads with this profile to talk to each
		// other, in the absence of any Policy.
		defaultProfile := api.NewProfile()
		defaultProfile.Name = "default"
		defaultProfile.Spec.LabelsToApply = map[string]string{"default": ""}
		defaultProfile.Spec.Egress = []api.Rule{{Action: api.Allow}}
		defaultProfile.Spec.Ingress = []api.Rule{{
			Action: api.Allow,
			Source: api.EntityRule{Selector: "default == ''"},
		}}
		_, err := client.Profiles().Create(utils.Ctx, defaultProfile, utils.NoOptions)
		Expect(err).NotTo(HaveOccurred())

		// Create three workloads, using that profile.
		for ii := range w {
			iiStr := strconv.Itoa(ii)
			w[ii] = workload.Run(felix, "w"+iiStr, "default", "10.65.0.1"+iiStr, "8055", "tcp")
			w[ii].Configure(client)
		}

		cc = &workload.ConnectivityChecker{}
	})

	AfterEach(func() {

		if CurrentGinkgoTestDescription().Failed {
			felix.Exec("iptables-save", "-c")
			felix.Exec("ip", "r")
		}

		for ii := range w {
			w[ii].Stop()
		}
		felix.Stop()

		if CurrentGinkgoTestDescription().Failed {
			etcd.Exec("etcdctl", "ls", "--recursive", "/")
		}
		etcd.Stop()
	})

	It("full connectivity to and from workload 0", func() {
		cc.ExpectSome(w[1], w[0])
		cc.ExpectSome(w[2], w[0])
		cc.ExpectSome(w[0], w[1])
		cc.ExpectSome(w[0], w[2])
		cc.CheckConnectivity()
	})

	It("connectivity via workload 0's NAT", func() {
		cc.ExpectSome(w[1], workload.IP("10.96.0.10"), 80)
		cc.CheckConnectivity()
	})

	Context("with ingress-only restriction for workload 0", func() {

		BeforeEach(func() {
			policy := api.NewNetworkPolicy()
			policy.Namespace = "fv"
			policy.Name = "policy-1"
			allowFromW1 := api.Rule{
				Action: api.Allow,
				Source: api.EntityRule{
					Selector: w[1].NameSelector(),
				},
			}
			policy.Spec.Ingress = []api.Rule{allowFromW1}
			policy.Spec.Selector = w[0].NameSelector()
			_, err := client.NetworkPolicies().Create(utils.Ctx, policy, utils.NoOptions)
			Expect(err).NotTo(HaveOccurred())
		})

		It("only w1 can connect into w0, but egress from w0 is unrestricted", func() {
			cc.ExpectNone(w[2], w[0])
			cc.ExpectSome(w[1], w[0])
			cc.ExpectSome(w[0], w[1])
			cc.ExpectSome(w[0], w[2])
			cc.CheckConnectivity()
		})
	})

	Context("with egress-only restriction for workload 0", func() {

		BeforeEach(func() {
			policy := api.NewNetworkPolicy()
			policy.Namespace = "fv"
			policy.Name = "policy-1"
			allowToW1 := api.Rule{
				Action: api.Allow,
				Destination: api.EntityRule{
					Selector: w[1].NameSelector(),
				},
			}
			policy.Spec.Egress = []api.Rule{allowToW1}
			policy.Spec.Selector = w[0].NameSelector()
			_, err := client.NetworkPolicies().Create(utils.Ctx, policy, utils.NoOptions)
			Expect(err).NotTo(HaveOccurred())
		})

		It("ingress to w0 is unrestricted, but w0 can only connect out to w1", func() {
			cc.ExpectNone(w[0], w[2])
			cc.ExpectSome(w[1], w[0])
			cc.ExpectSome(w[2], w[0])
			cc.ExpectSome(w[0], w[1])
			cc.CheckConnectivity()
		})
	})

	Context("with ingress rules and types [ingress,egress]", func() {

		BeforeEach(func() {
			policy := api.NewNetworkPolicy()
			policy.Namespace = "fv"
			policy.Name = "policy-1"
			allowFromW1 := api.Rule{
				Action: api.Allow,
				Source: api.EntityRule{
					Selector: w[1].NameSelector(),
				},
			}
			policy.Spec.Ingress = []api.Rule{allowFromW1}
			policy.Spec.Selector = w[0].NameSelector()
			policy.Spec.Types = []api.PolicyType{api.PolicyTypeIngress, api.PolicyTypeEgress}
			_, err := client.NetworkPolicies().Create(utils.Ctx, policy, utils.NoOptions)
			Expect(err).NotTo(HaveOccurred())
		})

		It("only w1 can connect into w0, and all egress from w0 is denied", func() {
			cc.ExpectNone(w[2], w[0])
			cc.ExpectSome(w[1], w[0])
			cc.ExpectNone(w[0], w[1])
			cc.ExpectNone(w[0], w[2])
			cc.CheckConnectivity()
		})
	})

	Context("with an egress deny rule", func() {
		var policy *api.NetworkPolicy

		BeforeEach(func() {
			policy = api.NewNetworkPolicy()
			policy.Namespace = "fv"
			policy.Name = "policy-1"
			allowFromW1 := api.Rule{
				Action: api.Allow,
				Source: api.EntityRule{
					Selector: w[1].NameSelector(),
				},
			}
			policy.Spec.Ingress = []api.Rule{allowFromW1}
			policy.Spec.Egress = []api.Rule{{Action: api.Deny}}
			policy.Spec.Selector = w[0].NameSelector()
		})

		JustBeforeEach(func() {
			_, err := client.NetworkPolicies().Create(utils.Ctx, policy, utils.NoOptions)
			Expect(err).NotTo(HaveOccurred())
		})

		Describe("and types [ingress] (i.e. disabling the egress rule)", func() {
			BeforeEach(func() {
				policy.Spec.Types = []api.PolicyType{api.PolicyTypeIngress}
			})

			It("only w1 can connect into w0, and all egress from w0 is allowed", func() {
				cc.ExpectNone(w[2], w[0])
				cc.ExpectSome(w[1], w[0])
				cc.ExpectSome(w[0], w[1])
				cc.ExpectSome(w[0], w[2])
				cc.CheckConnectivity()
			})
		})

		Describe("and types [ingress, egress]", func() {
			BeforeEach(func() {
				policy.Spec.Types = []api.PolicyType{api.PolicyTypeIngress, api.PolicyTypeEgress}
			})

			It("only w1 can connect into w0, and all egress from w0 is blocked", func() {
				cc.ExpectNone(w[2], w[0])
				cc.ExpectSome(w[1], w[0])
				cc.ExpectNone(w[0], w[1])
				cc.ExpectNone(w[0], w[2])
				cc.CheckConnectivity()
			})
		})
	})
})

var _ = Context("with Typha and Felix-Typha TLS", func() {

	var (
		etcd   *containers.Container
		felix  *infrastructure.Felix
		client client.Interface
		w      [3]*workload.Workload
		cc     *workload.ConnectivityChecker
	)

	BeforeEach(func() {
		options := infrastructure.DefaultTopologyOptions()
		options.WithTypha = true
		options.WithFelixTyphaTLS = true
		felix, etcd, client = infrastructure.StartSingleNodeEtcdTopology(options)

		// Install a default profile that allows workloads with this profile to talk to each
		// other, in the absence of any Policy.
		defaultProfile := api.NewProfile()
		defaultProfile.Name = "default"
		defaultProfile.Spec.LabelsToApply = map[string]string{"default": ""}
		defaultProfile.Spec.Egress = []api.Rule{{Action: api.Allow}}
		defaultProfile.Spec.Ingress = []api.Rule{{
			Action: api.Allow,
			Source: api.EntityRule{Selector: "default == ''"},
		}}
		_, err := client.Profiles().Create(utils.Ctx, defaultProfile, utils.NoOptions)
		Expect(err).NotTo(HaveOccurred())

		// Create three workloads, using that profile.
		for ii := range w {
			iiStr := strconv.Itoa(ii)
			w[ii] = workload.Run(felix, "w"+iiStr, "default", "10.65.0.1"+iiStr, "8055", "tcp")
			w[ii].Configure(client)
		}

		cc = &workload.ConnectivityChecker{}
	})

	AfterEach(func() {

		if CurrentGinkgoTestDescription().Failed {
			felix.Exec("iptables-save", "-c")
			felix.Exec("ip", "r")
		}

		for ii := range w {
			w[ii].Stop()
		}
		felix.Stop()

		if CurrentGinkgoTestDescription().Failed {
			etcd.Exec("etcdctl", "ls", "--recursive", "/")
		}
		etcd.Stop()
	})

	It("full connectivity to and from workload 0", func() {
		cc.ExpectSome(w[1], w[0])
		cc.ExpectSome(w[2], w[0])
		cc.ExpectSome(w[0], w[1])
		cc.ExpectSome(w[0], w[2])
		cc.CheckConnectivity()
	})

	Context("with ingress-only restriction for workload 0", func() {

		BeforeEach(func() {
			policy := api.NewNetworkPolicy()
			policy.Namespace = "fv"
			policy.Name = "policy-1"
			allowFromW1 := api.Rule{
				Action: api.Allow,
				Source: api.EntityRule{
					Selector: w[1].NameSelector(),
				},
			}
			policy.Spec.Ingress = []api.Rule{allowFromW1}
			policy.Spec.Selector = w[0].NameSelector()
			_, err := client.NetworkPolicies().Create(utils.Ctx, policy, utils.NoOptions)
			Expect(err).NotTo(HaveOccurred())
		})

		It("only w1 can connect into w0, but egress from w0 is unrestricted", func() {
			cc.ExpectNone(w[2], w[0])
			cc.ExpectSome(w[1], w[0])
			cc.ExpectSome(w[0], w[1])
			cc.ExpectSome(w[0], w[2])
			cc.CheckConnectivity()
		})
	})
})
<commit_msg>Remove prototype BPF test.<commit_after>// +build fvtests

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

package fv_test

import (
	"strconv"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"

	"github.com/projectcalico/felix/fv/containers"
	"github.com/projectcalico/felix/fv/infrastructure"
	"github.com/projectcalico/felix/fv/utils"
	"github.com/projectcalico/felix/fv/workload"
	api "github.com/projectcalico/libcalico-go/lib/apis/v3"
	client "github.com/projectcalico/libcalico-go/lib/clientv3"
)

var _ = Context("_INGRESS-EGRESS_ _BPF-SAFE_ with initialized Felix, etcd datastore, 3 workloads", func() {

	var (
		etcd   *containers.Container
		felix  *infrastructure.Felix
		client client.Interface
		w      [3]*workload.Workload
		cc     *workload.ConnectivityChecker
	)

	BeforeEach(func() {
		felix, etcd, client = infrastructure.StartSingleNodeEtcdTopology(infrastructure.DefaultTopologyOptions())

		// Install a default profile that allows workloads with this profile to talk to each
		// other, in the absence of any Policy.
		defaultProfile := api.NewProfile()
		defaultProfile.Name = "default"
		defaultProfile.Spec.LabelsToApply = map[string]string{"default": ""}
		defaultProfile.Spec.Egress = []api.Rule{{Action: api.Allow}}
		defaultProfile.Spec.Ingress = []api.Rule{{
			Action: api.Allow,
			Source: api.EntityRule{Selector: "default == ''"},
		}}
		_, err := client.Profiles().Create(utils.Ctx, defaultProfile, utils.NoOptions)
		Expect(err).NotTo(HaveOccurred())

		// Create three workloads, using that profile.
		for ii := range w {
			iiStr := strconv.Itoa(ii)
			w[ii] = workload.Run(felix, "w"+iiStr, "default", "10.65.0.1"+iiStr, "8055", "tcp")
			w[ii].Configure(client)
		}

		cc = &workload.ConnectivityChecker{}
	})

	AfterEach(func() {

		if CurrentGinkgoTestDescription().Failed {
			felix.Exec("iptables-save", "-c")
			felix.Exec("ip", "r")
		}

		for ii := range w {
			w[ii].Stop()
		}
		felix.Stop()

		if CurrentGinkgoTestDescription().Failed {
			etcd.Exec("etcdctl", "ls", "--recursive", "/")
		}
		etcd.Stop()
	})

	It("full connectivity to and from workload 0", func() {
		cc.ExpectSome(w[1], w[0])
		cc.ExpectSome(w[2], w[0])
		cc.ExpectSome(w[0], w[1])
		cc.ExpectSome(w[0], w[2])
		cc.CheckConnectivity()
	})

	Context("with ingress-only restriction for workload 0", func() {

		BeforeEach(func() {
			policy := api.NewNetworkPolicy()
			policy.Namespace = "fv"
			policy.Name = "policy-1"
			allowFromW1 := api.Rule{
				Action: api.Allow,
				Source: api.EntityRule{
					Selector: w[1].NameSelector(),
				},
			}
			policy.Spec.Ingress = []api.Rule{allowFromW1}
			policy.Spec.Selector = w[0].NameSelector()
			_, err := client.NetworkPolicies().Create(utils.Ctx, policy, utils.NoOptions)
			Expect(err).NotTo(HaveOccurred())
		})

		It("only w1 can connect into w0, but egress from w0 is unrestricted", func() {
			cc.ExpectNone(w[2], w[0])
			cc.ExpectSome(w[1], w[0])
			cc.ExpectSome(w[0], w[1])
			cc.ExpectSome(w[0], w[2])
			cc.CheckConnectivity()
		})
	})

	Context("with egress-only restriction for workload 0", func() {

		BeforeEach(func() {
			policy := api.NewNetworkPolicy()
			policy.Namespace = "fv"
			policy.Name = "policy-1"
			allowToW1 := api.Rule{
				Action: api.Allow,
				Destination: api.EntityRule{
					Selector: w[1].NameSelector(),
				},
			}
			policy.Spec.Egress = []api.Rule{allowToW1}
			policy.Spec.Selector = w[0].NameSelector()
			_, err := client.NetworkPolicies().Create(utils.Ctx, policy, utils.NoOptions)
			Expect(err).NotTo(HaveOccurred())
		})

		It("ingress to w0 is unrestricted, but w0 can only connect out to w1", func() {
			cc.ExpectNone(w[0], w[2])
			cc.ExpectSome(w[1], w[0])
			cc.ExpectSome(w[2], w[0])
			cc.ExpectSome(w[0], w[1])
			cc.CheckConnectivity()
		})
	})

	Context("with ingress rules and types [ingress,egress]", func() {

		BeforeEach(func() {
			policy := api.NewNetworkPolicy()
			policy.Namespace = "fv"
			policy.Name = "policy-1"
			allowFromW1 := api.Rule{
				Action: api.Allow,
				Source: api.EntityRule{
					Selector: w[1].NameSelector(),
				},
			}
			policy.Spec.Ingress = []api.Rule{allowFromW1}
			policy.Spec.Selector = w[0].NameSelector()
			policy.Spec.Types = []api.PolicyType{api.PolicyTypeIngress, api.PolicyTypeEgress}
			_, err := client.NetworkPolicies().Create(utils.Ctx, policy, utils.NoOptions)
			Expect(err).NotTo(HaveOccurred())
		})

		It("only w1 can connect into w0, and all egress from w0 is denied", func() {
			cc.ExpectNone(w[2], w[0])
			cc.ExpectSome(w[1], w[0])
			cc.ExpectNone(w[0], w[1])
			cc.ExpectNone(w[0], w[2])
			cc.CheckConnectivity()
		})
	})

	Context("with an egress deny rule", func() {
		var policy *api.NetworkPolicy

		BeforeEach(func() {
			policy = api.NewNetworkPolicy()
			policy.Namespace = "fv"
			policy.Name = "policy-1"
			allowFromW1 := api.Rule{
				Action: api.Allow,
				Source: api.EntityRule{
					Selector: w[1].NameSelector(),
				},
			}
			policy.Spec.Ingress = []api.Rule{allowFromW1}
			policy.Spec.Egress = []api.Rule{{Action: api.Deny}}
			policy.Spec.Selector = w[0].NameSelector()
		})

		JustBeforeEach(func() {
			_, err := client.NetworkPolicies().Create(utils.Ctx, policy, utils.NoOptions)
			Expect(err).NotTo(HaveOccurred())
		})

		Describe("and types [ingress] (i.e. disabling the egress rule)", func() {
			BeforeEach(func() {
				policy.Spec.Types = []api.PolicyType{api.PolicyTypeIngress}
			})

			It("only w1 can connect into w0, and all egress from w0 is allowed", func() {
				cc.ExpectNone(w[2], w[0])
				cc.ExpectSome(w[1], w[0])
				cc.ExpectSome(w[0], w[1])
				cc.ExpectSome(w[0], w[2])
				cc.CheckConnectivity()
			})
		})

		Describe("and types [ingress, egress]", func() {
			BeforeEach(func() {
				policy.Spec.Types = []api.PolicyType{api.PolicyTypeIngress, api.PolicyTypeEgress}
			})

			It("only w1 can connect into w0, and all egress from w0 is blocked", func() {
				cc.ExpectNone(w[2], w[0])
				cc.ExpectSome(w[1], w[0])
				cc.ExpectNone(w[0], w[1])
				cc.ExpectNone(w[0], w[2])
				cc.CheckConnectivity()
			})
		})
	})
})

var _ = Context("with Typha and Felix-Typha TLS", func() {

	var (
		etcd   *containers.Container
		felix  *infrastructure.Felix
		client client.Interface
		w      [3]*workload.Workload
		cc     *workload.ConnectivityChecker
	)

	BeforeEach(func() {
		options := infrastructure.DefaultTopologyOptions()
		options.WithTypha = true
		options.WithFelixTyphaTLS = true
		felix, etcd, client = infrastructure.StartSingleNodeEtcdTopology(options)

		// Install a default profile that allows workloads with this profile to talk to each
		// other, in the absence of any Policy.
		defaultProfile := api.NewProfile()
		defaultProfile.Name = "default"
		defaultProfile.Spec.LabelsToApply = map[string]string{"default": ""}
		defaultProfile.Spec.Egress = []api.Rule{{Action: api.Allow}}
		defaultProfile.Spec.Ingress = []api.Rule{{
			Action: api.Allow,
			Source: api.EntityRule{Selector: "default == ''"},
		}}
		_, err := client.Profiles().Create(utils.Ctx, defaultProfile, utils.NoOptions)
		Expect(err).NotTo(HaveOccurred())

		// Create three workloads, using that profile.
		for ii := range w {
			iiStr := strconv.Itoa(ii)
			w[ii] = workload.Run(felix, "w"+iiStr, "default", "10.65.0.1"+iiStr, "8055", "tcp")
			w[ii].Configure(client)
		}

		cc = &workload.ConnectivityChecker{}
	})

	AfterEach(func() {

		if CurrentGinkgoTestDescription().Failed {
			felix.Exec("iptables-save", "-c")
			felix.Exec("ip", "r")
		}

		for ii := range w {
			w[ii].Stop()
		}
		felix.Stop()

		if CurrentGinkgoTestDescription().Failed {
			etcd.Exec("etcdctl", "ls", "--recursive", "/")
		}
		etcd.Stop()
	})

	It("full connectivity to and from workload 0", func() {
		cc.ExpectSome(w[1], w[0])
		cc.ExpectSome(w[2], w[0])
		cc.ExpectSome(w[0], w[1])
		cc.ExpectSome(w[0], w[2])
		cc.CheckConnectivity()
	})

	Context("with ingress-only restriction for workload 0", func() {

		BeforeEach(func() {
			policy := api.NewNetworkPolicy()
			policy.Namespace = "fv"
			policy.Name = "policy-1"
			allowFromW1 := api.Rule{
				Action: api.Allow,
				Source: api.EntityRule{
					Selector: w[1].NameSelector(),
				},
			}
			policy.Spec.Ingress = []api.Rule{allowFromW1}
			policy.Spec.Selector = w[0].NameSelector()
			_, err := client.NetworkPolicies().Create(utils.Ctx, policy, utils.NoOptions)
			Expect(err).NotTo(HaveOccurred())
		})

		It("only w1 can connect into w0, but egress from w0 is unrestricted", func() {
			cc.ExpectNone(w[2], w[0])
			cc.ExpectSome(w[1], w[0])
			cc.ExpectSome(w[0], w[1])
			cc.ExpectSome(w[0], w[2])
			cc.CheckConnectivity()
		})
	})
})
<|endoftext|>


<commit_before>package html

import (
	"html/template"

	"github.com/russross/blackfriday"
)

// CSS prevents any custom embedded styles from being encoded to html safe values
func CSS(s string) template.CSS {
	return template.CSS(s)
}

// Preview returns a preview of the string
func Preview(size int, val string) string {
	if len(val) <= size {
		return val
	}
	return val[:size-1] + "..."
}

// Markdown converts text in the markdown syntax to html
func Markdown(input string) interface{} {
	out := string(blackfriday.MarkdownCommon([]byte(input)))
	return template.HTML(out)
}

// Add adds the numbers
func Add(a, b int) int {
	return a + b
}
<commit_msg>add some common html template functions<commit_after>package html

import (
	"html/template"
	"time"

	"github.com/chrisolsen/ae/model"
	"github.com/russross/blackfriday"
	"google.golang.org/appengine/datastore"
)

// CSS prevents any custom embedded styles from being encoded to html safe values
func CSS(s string) template.CSS {
	return template.CSS(s)
}

// Preview returns a preview of the string
func Preview(size int, val string) string {
	if len(val) <= size {
		return val
	}
	return val[:size-1] + "..."
}

// Markdown converts text in the markdown syntax to html
func Markdown(input string) interface{} {
	out := string(blackfriday.MarkdownCommon([]byte(input)))
	return template.HTML(out)
}

// Add adds the numbers
func Add(a, b int) int {
	return a + b
}

// EncodeKey encodes a datastore key
func EncodeKey(data interface{}) string {
	switch data.(type) {
	case model.Base:
		return data.(model.Base).Key.Encode()
	case *datastore.Key:
		return data.(*datastore.Key).Encode()
	default:
		return ""
	}
}

// Checked returns the checked attribute for positive values.
// 	<input type="checkbox" {{IsAdmin | checked}}> => <input type="checkbox" checked="checked">
func Checked(checked bool) template.HTMLAttr {
	if checked {
		return template.HTMLAttr(`checked="checked"`)
	}
	return ""
}

// Disabled returns the checked attribute for positive values.
// 	<button type="submit" {{HasError | disabled}}>Save</button> => <button type="submit" disabled="">Save</button>
// 	or
// 	<button type="submit" {{ValidationError | disabled}}>Save</button> => <button type="submit" disabled="">Save</button>
func Disabled(err interface{}) template.HTMLAttr {
	d := template.HTMLAttr(`disabled="disabled"`)
	switch err.(type) {
	case string:
		if len(err.(string)) == 0 {
			return ""
		}
		return d
	case error:
		return d
	default:
		return ""
	}
}

// KeyEqual allow for *datastore.Key comparison
func KeyEqual(a, b *datastore.Key) bool {
	return a.Equal(b)
}

// Timestamp formats the time to the RFC3339 layout
func Timestamp(d time.Time) string {
	return d.Format(time.RFC3339)
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"math/rand"
	"net/http"
	"net/url"
	"os"
	"path"
	"strings"
	"time"

	"github.com/PonyvilleFM/aura/internal/bot"
	"github.com/PonyvilleFM/aura/internal/commands/source"
	"github.com/PonyvilleFM/aura/internal/recording"
	"github.com/bwmarrin/discordgo"
	_ "github.com/joho/godotenv/autoload"
	hashids "github.com/speps/go-hashids"
)

func init() {
	rand.Seed(time.Now().UnixNano())
}

type aura struct {
	cs *bot.CommandSet
	s  *discordgo.Session

	guildRecordings map[string]*rec
	state           *state
	hid             *hashids.HashID
}

type state struct {
	DownloadURLs map[string]string // Guild ID -> URL
	PermRoles    map[string]string // Guild ID -> needed role ID
	Shorturls    map[string]string // hashid -> partial route
}

func (s *state) Save() error {
	fout, err := os.Create(path.Join(dataPrefix, "state.json"))
	if err != nil {
		return err
	}
	defer fout.Close()

	return json.NewEncoder(fout).Encode(s)
}

func (s *state) Load() error {
	fin, err := os.Open(path.Join(dataPrefix, "state.json"))
	if err != nil {
		return err
	}
	defer fin.Close()

	return json.NewDecoder(fin).Decode(s)
}

type rec struct {
	*recording.Recording
	creator string
}

const (
	djonHelp  = ``
	djoffHelp = ``
	setupHelp = ``
)

func (a *aura) Permissons(s *discordgo.Session, m *discordgo.Message, parv []string) error {
	ch, err := s.Channel(m.ChannelID)
	if err != nil {
		return err
	}

	gid := ch.GuildID
	role := a.state.PermRoles[gid]

	gu, err := s.GuildMember(gid, m.Author.ID)
	if err != nil {
		return err
	}

	found := false
	for _, r := range gu.Roles {
		if r == role {
			found = true
			break
		}
	}

	if !found {
		return errors.New("aura: no permissions")
	}

	return nil
}

func (a *aura) roles(s *discordgo.Session, m *discordgo.Message, parv []string) error {
	log.Println("got here")
	ch, err := s.Channel(m.ChannelID)
	if err != nil {
		return err
	}

	gid := ch.GuildID

	result := "Roles in this group:\n"

	roles, err := s.GuildRoles(gid)
	if err != nil {
		return err
	}

	for _, r := range roles {
		result += fmt.Sprintf("- %s: %s\n", r.ID, r.Name)
	}

	s.ChannelMessageSend(m.ChannelID, result)
	return nil
}

func (a *aura) setup(s *discordgo.Session, m *discordgo.Message, parv []string) error {
	if len(parv) != 3 {
		return errors.New("aura: wrong number of params for setup")
	}

	role := parv[1]
	url := parv[2]

	ch, err := s.Channel(m.ChannelID)
	if err != nil {
		return err
	}

	gid := ch.GuildID

	roles, err := s.GuildRoles(gid)
	if err != nil {
		return err
	}

	found := false
	for _, r := range roles {
		if r.ID == role {
			found = true
			break
		}
	}

	if !found {
		return errors.New("aura: Role not found")
	}

	a.state.PermRoles[gid] = role
	a.state.DownloadURLs[gid] = url

	s.ChannelMessageSend(m.ChannelID, fmt.Sprintf("Guild %s set up for recording url %s controlled by role %s", gid, url, role))

	a.state.Save()
	return nil
}

func (a *aura) djon(s *discordgo.Session, m *discordgo.Message, parv []string) error {
	ch, err := s.Channel(m.ChannelID)
	if err != nil {
		return err
	}

	gid := ch.GuildID
	creator := m.Author.Username

	member, err := s.GuildMember(gid, m.Author.ID)
	if err != nil {
		return err
	}

	if member.Nick != "" {
		creator = member.Nick
	}

	fname, err := genFname(creator)
	if err != nil {
		return err
	}

	_, ok := a.guildRecordings[gid]
	if ok {
		log.Println(a.guildRecordings)
		return errors.New("aura: another recording is already in progress")
	}

	os.Mkdir(path.Join(dataPrefix, gid), 0775)

	rr, err := recording.New(a.state.DownloadURLs[gid], path.Join(dataPrefix, gid, fname))
	if err != nil {
		return err
	}

	a.guildRecordings[gid] = &rec{
		Recording: rr,
		creator:   creator,
	}

	go func() {
		err := rr.Start()
		if err != nil {
			s.ChannelMessageSend(m.ChannelID, fmt.Sprintf("recording error: %v", err))
			return
		}
	}()

	s.ChannelMessageSend(m.ChannelID, fmt.Sprintf("Now recording: `%s`\n\n%s get in here fam", fname, os.Getenv("NOTIFICATION_SQUAD_ID")))

	inv, err := s.ChannelInviteCreate(gid, discordgo.Invite{
		MaxAge: 4800,
	})
	if err != nil {
		log.Println(err)
		return nil
	}

	invurl := "http://discord.gg/" + inv.Code

	err = announce("Live DJ on-air: " + creator + "\nJoin our chat here: " + invurl)
	if err != nil {
		log.Println(err)
		return nil
	}

	go a.waitAndAnnounce(s, m, a.guildRecordings[gid], gid)

	return nil
}

func (a *aura) waitAndAnnounce(s *discordgo.Session, m *discordgo.Message, r *rec, gid string) {
	<-r.Done()

	defer delete(a.guildRecordings, gid)

	fname := r.OutputFilename()
	parts := strings.Split(fname, "/")

	recurl := fmt.Sprintf("https://%s/var/%s/%s", recordingDomain, parts[1], urlencode(parts[2]))
	id, err := a.hid.EncodeInt64([]int64{int64(rand.Int())})
	if err != nil {
		s.ChannelMessageSend(m.ChannelID, fmt.Sprintf("This state should be impossible. Recording saved but unknown short URL: %v", err))
		return
	}

	a.state.Shorturls[id] = recurl
	a.state.Save()

	slink := fmt.Sprintf("https://%s/id/%s", recordingDomain, id)

	s.ChannelMessageSend(m.ChannelID, fmt.Sprintf("Recording complete (%s): %s", time.Now().Sub(r.StartTime()).String(), slink))

	sn := r.creator

	msg := fmt.Sprintf("New recording by %s: %s", sn, slink)
	err = announce(msg)
	if err != nil {
		log.Println(err)
		return
	}
}

func urlencode(inp string) string {
	return (&url.URL{Path: inp}).String()
}

func (a *aura) djoff(s *discordgo.Session, m *discordgo.Message, parv []string) error {
	ch, err := s.Channel(m.ChannelID)
	if err != nil {
		return err
	}

	gid := ch.GuildID

	r, ok := a.guildRecordings[gid]
	if r == nil || !ok {
		log.Println(a.guildRecordings)
		return errors.New("aura: no recording is currently in progress")
	}

	if r.Err == nil {
		s.ChannelMessageSend(m.ChannelID, "Finishing recording (waiting 30 seconds)")
		time.Sleep(30 * time.Second)

		r.Cancel()
	}

	return nil
}

func (a *aura) Handle(s *discordgo.Session, m *discordgo.MessageCreate) {
	err := a.cs.Run(s, m.Message)
	if err != nil {
		log.Println(err)
	}
}

var (
	token           = os.Getenv("TOKEN")
	dataPrefix      = os.Getenv("DATA_PREFIX")
	recordingDomain = os.Getenv("RECORDING_DOMAIN")
	hashidsSalt     = os.Getenv("HASHIDS_SALT")
	port            = os.Getenv("PORT")
)

func main() {
	dg, err := discordgo.New("Bot " + token)
	if err != nil {
		log.Fatal(err)
	}

	hid := hashids.NewData()
	hid.Salt = hashidsSalt

	hiid, err := hashids.NewWithData(hid)
	if err != nil {
		log.Fatal(err)
	}

	a := &aura{
		cs:              bot.NewCommandSet(),
		s:               dg,
		guildRecordings: map[string]*rec{},

		hid: hiid,

		state: &state{
			DownloadURLs: map[string]string{},
			PermRoles:    map[string]string{},
			Shorturls:    map[string]string{},
		},
	}

	err = a.state.Load()
	if err != nil {
		log.Println(err)
	}

	a.cs.AddCmd("roles", "", bot.NoPermissions, a.roles)
	a.cs.AddCmd("setup", setupHelp, bot.NoPermissions, a.setup)
	a.cs.AddCmd("djon", djonHelp, a.Permissons, a.djon)
	a.cs.AddCmd("djoff", djoffHelp, a.Permissons, a.djoff)
	a.cs.AddCmd("source", "Source code information", bot.NoPermissions, source.Source)

	dg.AddHandler(a.Handle)
	dg.AddHandler(messageCreate)

	err = dg.Open()
	if err != nil {
		log.Fatal(err)
	}

	log.Println("ready")

	http.Handle("/id/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		id := r.RequestURI[4:]

		redir, ok := a.state.Shorturls[id]
		if !ok {
			http.Error(w, "not found, sorry", http.StatusNotFound)
			return
		}

		http.Redirect(w, r, redir, http.StatusFound)
	}))

	http.HandleFunc("/links.json", func(w http.ResponseWriter, r *http.Request) {
		json.NewEncoder(w).Encode(a.state.Shorturls)
	})

	http.Handle("/var/", http.FileServer(http.Dir(".")))
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		http.ServeFile(w, r, "./index.html")
	})

	http.ListenAndServe(":"+port, nil)
}

// This function will be called (due to AddHandler above) every time a new
// message is created on any channel that the autenticated bot has access to.
func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) {
	// Print message to stdout.
	fmt.Printf("%20s %20s %20s > %s\n", m.ChannelID, time.Now().Format(time.Stamp), m.Author.Username, m.Content)
}
<commit_msg>Update aura.go<commit_after>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"math/rand"
	"net/http"
	"net/url"
	"os"
	"path"
	"strings"
	"time"

	"github.com/PonyvilleFM/aura/internal/bot"
	"github.com/PonyvilleFM/aura/internal/commands/source"
	"github.com/PonyvilleFM/aura/internal/recording"
	"github.com/bwmarrin/discordgo"
	_ "github.com/joho/godotenv/autoload"
	hashids "github.com/speps/go-hashids"
)

func init() {
	rand.Seed(time.Now().UnixNano())
}

type aura struct {
	cs *bot.CommandSet
	s  *discordgo.Session

	guildRecordings map[string]*rec
	state           *state
	hid             *hashids.HashID
}

type state struct {
	DownloadURLs map[string]string // Guild ID -> URL
	PermRoles    map[string]string // Guild ID -> needed role ID
	Shorturls    map[string]string // hashid -> partial route
}

func (s *state) Save() error {
	fout, err := os.Create(path.Join(dataPrefix, "state.json"))
	if err != nil {
		return err
	}
	defer fout.Close()

	return json.NewEncoder(fout).Encode(s)
}

func (s *state) Load() error {
	fin, err := os.Open(path.Join(dataPrefix, "state.json"))
	if err != nil {
		return err
	}
	defer fin.Close()

	return json.NewDecoder(fin).Decode(s)
}

type rec struct {
	*recording.Recording
	creator string
}

const (
	djonHelp  = ``
	djoffHelp = ``
	setupHelp = ``
)

func (a *aura) Permissons(s *discordgo.Session, m *discordgo.Message, parv []string) error {
	ch, err := s.Channel(m.ChannelID)
	if err != nil {
		return err
	}

	gid := ch.GuildID
	role := a.state.PermRoles[gid]

	gu, err := s.GuildMember(gid, m.Author.ID)
	if err != nil {
		return err
	}

	found := false
	for _, r := range gu.Roles {
		if r == role {
			found = true
			break
		}
	}

	if !found {
		return errors.New("aura: no permissions")
	}

	return nil
}

func (a *aura) roles(s *discordgo.Session, m *discordgo.Message, parv []string) error {
	log.Println("got here")
	ch, err := s.Channel(m.ChannelID)
	if err != nil {
		return err
	}

	gid := ch.GuildID

	result := "Roles in this group:\n"

	roles, err := s.GuildRoles(gid)
	if err != nil {
		return err
	}

	for _, r := range roles {
		result += fmt.Sprintf("- %s: %s\n", r.ID, r.Name)
	}

	s.ChannelMessageSend(m.ChannelID, result)
	return nil
}

func (a *aura) setup(s *discordgo.Session, m *discordgo.Message, parv []string) error {
	if len(parv) != 3 {
		return errors.New("aura: wrong number of params for setup")
	}

	role := parv[1]
	url := parv[2]

	ch, err := s.Channel(m.ChannelID)
	if err != nil {
		return err
	}

	gid := ch.GuildID

	roles, err := s.GuildRoles(gid)
	if err != nil {
		return err
	}

	found := false
	for _, r := range roles {
		if r.ID == role {
			found = true
			break
		}
	}

	if !found {
		return errors.New("aura: Role not found")
	}

	a.state.PermRoles[gid] = role
	a.state.DownloadURLs[gid] = url

	s.ChannelMessageSend(m.ChannelID, fmt.Sprintf("Guild %s set up for recording url %s controlled by role %s", gid, url, role))

	a.state.Save()
	return nil
}

func (a *aura) djon(s *discordgo.Session, m *discordgo.Message, parv []string) error {
	ch, err := s.Channel(m.ChannelID)
	if err != nil {
		return err
	}

	gid := ch.GuildID
	creator := m.Author.Username

	member, err := s.GuildMember(gid, m.Author.ID)
	if err != nil {
		return err
	}

	if member.Nick != "" {
		creator = member.Nick
	}

	fname, err := genFname(creator)
	if err != nil {
		return err
	}

	_, ok := a.guildRecordings[gid]
	if ok {
		log.Println(a.guildRecordings)
		return errors.New("aura: another recording is already in progress")
	}

	os.Mkdir(path.Join(dataPrefix, gid), 0775)

	rr, err := recording.New(a.state.DownloadURLs[gid], path.Join(dataPrefix, gid, fname))
	if err != nil {
		return err
	}

	a.guildRecordings[gid] = &rec{
		Recording: rr,
		creator:   creator,
	}

	go func() {
		err := rr.Start()
		if err != nil {
			s.ChannelMessageSend(m.ChannelID, fmt.Sprintf("recording error: %v", err))
			return
		}
	}()

	s.ChannelMessageSend(m.ChannelID, fmt.Sprintf("Now recording: `%s`\n\n%s get in here fam", fname, os.Getenv("NOTIFICATION_SQUAD_ID")))

	inv, err := s.ChannelInviteCreate(gid, discordgo.Invite{
		MaxAge: 4800,
	})
	if err != nil {
		log.Println(err)
		return nil
	}

	invurl := "http://discord.gg/" + inv.Code

	err = announce("Live DJ on-air: " + creator + "\nJoin our chat here: " + invurl)
	if err != nil {
		log.Println(err)
		return nil
	}

	go a.waitAndAnnounce(s, m, a.guildRecordings[gid], gid)

	return nil
}

func (a *aura) waitAndAnnounce(s *discordgo.Session, m *discordgo.Message, r *rec, gid string) {
	<-r.Done()

	defer delete(a.guildRecordings, gid)

	fname := r.OutputFilename()
	parts := strings.Split(fname, "/")

	recurl := fmt.Sprintf("https://%s/var/%s/%s", recordingDomain, parts[1], urlencode(parts[2]))
	id, err := a.hid.EncodeInt64([]int64{int64(rand.Int())})
	if err != nil {
		s.ChannelMessageSend(m.ChannelID, fmt.Sprintf("This state should be impossible. Recording saved but unknown short URL: %v", err))
		return
	}

	a.state.Shorturls[id] = recurl
	a.state.Save()

	slink := fmt.Sprintf("https://%s/id/%s", recordingDomain, id)

	s.ChannelMessageSend(m.ChannelID, fmt.Sprintf("Recording complete (%s): %s", time.Now().Sub(r.StartTime()).String(), slink))

	sn := r.creator

	msg := fmt.Sprintf("New recording by %s: %s", sn, slink)
	err = announce(msg)
	if err != nil {
		log.Println(err)
		return
	}
}

func urlencode(inp string) string {
	return (&url.URL{Path: inp}).String()
}

func (a *aura) djoff(s *discordgo.Session, m *discordgo.Message, parv []string) error {
	ch, err := s.Channel(m.ChannelID)
	if err != nil {
		return err
	}

	gid := ch.GuildID

	r, ok := a.guildRecordings[gid]
	if r == nil || !ok {
		log.Println(a.guildRecordings)
		return errors.New("aura: no recording is currently in progress")
	}

	if r.Err == nil {
		s.ChannelMessageSend(m.ChannelID, "Finishing recording (waiting 30 seconds)")
		time.Sleep(30 * time.Second)

		r.Cancel()
	}

	return nil
}

func (a *aura) Handle(s *discordgo.Session, m *discordgo.MessageCreate) {
	err := a.cs.Run(s, m.Message)
	if err != nil {
		log.Println(err)
	}
}

var (
	token           = os.Getenv("TOKEN")
	dataPrefix      = os.Getenv("DATA_PREFIX")
	recordingDomain = os.Getenv("RECORDING_DOMAIN")
	hashidsSalt     = os.Getenv("HASHIDS_SALT")
	port            = os.Getenv("PORT")
)

func main() {
	dg, err := discordgo.New("Bot " + token)
	if err != nil {
		log.Fatal(err)
	}

	hid := hashids.NewData()
	hid.Salt = hashidsSalt

	hiid, err := hashids.NewWithData(hid)
	if err != nil {
		log.Fatal(err)
	}

	a := &aura{
		cs:              bot.NewCommandSet(),
		s:               dg,
		guildRecordings: map[string]*rec{},

		hid: hiid,

		state: &state{
			DownloadURLs: map[string]string{},
			PermRoles:    map[string]string{},
			Shorturls:    map[string]string{},
		},
	}

	err = a.state.Load()
	if err != nil {
		log.Println(err)
	}

	a.cs.AddCmd("roles", "", bot.NoPermissions, a.roles)
	a.cs.AddCmd("setup", setupHelp, bot.NoPermissions, a.setup)
	a.cs.AddCmd("djon", djonHelp, a.Permissons, a.djon)
	a.cs.AddCmd("djoff", djoffHelp, a.Permissons, a.djoff)
	a.cs.AddCmd("source", "Source code information", bot.NoPermissions, source.Source)

	dg.AddHandler(a.Handle)
	dg.AddHandler(messageCreate)

	err = dg.Open()
	if err != nil {
		log.Fatal(err)
	}

	log.Println("ready")

	http.Handle("/id/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		id := r.RequestURI[4:]

		redir, ok := a.state.Shorturls[id]
		if !ok {
			http.Error(w, "not found, sorry", http.StatusNotFound)
			return
		}

		http.Redirect(w, r, redir, http.StatusFound)
	}))

	http.HandleFunc("/links.json", func(w http.ResponseWriter, r *http.Request) {
		json.NewEncoder(w).Encode(a.state.Shorturls)
	})

	http.Handle("/BronyRadio/", http.FileServer(http.Dir(".")))
	http.Handle("/sleepypony/", http.FileServer(http.Dir(".")))
	http.Handle("/toastbeard/", http.FileServer(http.Dir(".")))
	http.Handle("/var/", http.FileServer(http.Dir(".")))
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		http.ServeFile(w, r, "./index.html")
	})

	http.ListenAndServe(":"+port, nil)
}

// This function will be called (due to AddHandler above) every time a new
// message is created on any channel that the autenticated bot has access to.
func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) {
	// Print message to stdout.
	fmt.Printf("%20s %20s %20s > %s\n", m.ChannelID, time.Now().Format(time.Stamp), m.Author.Username, m.Content)
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"log"
	"math"
	"math/rand"
	"os"
	"sort"
	"time"

	"github.com/dt/go-metrics"
	"github.com/dt/thile/client"
	"github.com/dt/thile/gen"
	"github.com/foursquare/gohfile"
)

func RandomReq(name string, min, max int) *gen.SingleHFileKeyRequest {
	count := 10
	if false {
		count = int(math.Abs(rand.NormFloat64())*3 + 3)
	}
	keys := make([]int, count)
	lim := min + max

	for i := 0; i < count; i++ {
		k := rand.Intn(lim) + min
		keys[i] = k
	}
	sort.Ints(keys)

	keyBytes := make([][]byte, len(keys))
	for i, v := range keys {
		keyBytes[i] = hfile.MockKeyInt(v)
	}
	return &gen.SingleHFileKeyRequest{&name, keyBytes, nil, nil}
}

func Send(client *gen.HFileServiceClient, r *gen.SingleHFileKeyRequest) {
	var err error
	_, err = client.GetValuesSingle(r)

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

func generator(work chan bool, qps int, d metrics.Meter, q metrics.Gauge) {
	second := time.Second.Nanoseconds()
	pause := time.Nanosecond * time.Duration(second/int64(qps))

	for _ = range time.Tick(pause) {
		q.Update(int64(len(work)))
		select {
		case work <- true:
		default:
			d.Mark(1)
		}
	}
}

func SendOne(url, name string, min, max int, m metrics.Timer) {
	start := time.Now()
	r := RandomReq(name, min, max)
	c := thttp.NewThriftHttpRpcClient(url)
	if true {
		Send(c, r)
	} else {
		time.Sleep(2 * time.Millisecond)
	}
	m.UpdateSince(start)
}

func worker(work chan bool, url, name string, min, max int, m metrics.Timer) {
	for true {
		<-work
		SendOne(url, name, min, max, m)
	}
}

func main() {
	url := flag.String("url", "http://localhost:9999/rpc/HFileService", "URL of hfile server")
	name := flag.String("name", "testdata", "name of collection")
	min := flag.Int("min", 0, "min int key")
	max := flag.Int("max", -1, "max int key (required)")
	workers := flag.Int("workers", 8, "worker pool size")

	qps := flag.Int("qps", 100, "qps to attempt")

	flag.Parse()

	if *max < 0 {
		log.Fatal("max must be specified and >0")
	}

	w := make(chan bool, (*qps)*(*workers)*3)
	r := metrics.NewRegistry()
	m := metrics.GetOrRegisterTimer("rtt", r)
	d := metrics.GetOrRegisterMeter("dropped", r)
	q := metrics.GetOrRegisterGauge("queue", r)

	go metrics.LogScaled(r, time.Second*5, time.Millisecond, log.New(os.Stderr, "\t", 0))

	for i := 0; i < *workers; i++ {
		go worker(w, *url, *name, *min, *max, m)
	}

	generator(w, *qps, d, q)

}
<commit_msg>re-write load generator to use server-provided random keys<commit_after>package main

import (
	"flag"
	"fmt"
	"log"
	"math/rand"
	"os"
	"sort"
	"strings"
	"time"

	"github.com/dt/go-metrics-reporting"
	"github.com/dt/thile/client"
	"github.com/dt/thile/gen"
)

type Load struct {
	collection string
	sample     *int64
	keys       [][]byte

	server string

	work chan bool

	rtt       report.Timer
	dropped   report.Meter
	queueSize report.Guage
}

func (l *Load) setKeys() error {
	c := thttp.NewThriftHttpRpcClient(l.server)
	r := &gen.InfoRequest{&l.collection, l.sample}

	if resp, err := c.GetInfo(r); err != nil {
		return err
	} else {
		if len(resp) < 1 || len(resp[0].RandomKeys) < 1 {
			return fmt.Errorf("Response (len %d) contained no keys!", len(resp))
		}
		l.keys = resp[0].RandomKeys
		return nil
	}
}

func (l *Load) generator(qps int) {
	pause := time.Duration(time.Second.Nanoseconds() / int64(qps))

	for _ = range time.Tick(pause) {
		l.queueSize.Update(int64(len(l.work)))
		select {
		case l.work <- true:
		default:
			l.dropped.Mark(1)
		}
	}
}

func (l *Load) sendOne(client *gen.HFileServiceClient) {
	// TODO: randomly change up request type generated
	l.sendSingle(client)
}

func (l *Load) sendSingle(client *gen.HFileServiceClient) {
	r := &gen.SingleHFileKeyRequest{HfileName: &l.collection, SortedKeys: l.randomKeys(1)}
	before := time.Now()
	_, err := client.GetValuesSingle(r)
	if err != nil {
		log.Println("Error fetching value:", err)
	}
	l.rtt.UpdateSince(before)
}

func (l *Load) randomKeys(count int) [][]byte {
	indexes := make([]int, count)

	for i := 0; i < count; i++ {
		indexes[i] = rand.Intn(len(l.keys))
	}
	sort.Ints(indexes)

	out := make([][]byte, count)
	for i := 0; i < count; i++ {
		out[i] = l.keys[indexes[i]]
	}
	return out
}

func (l *Load) startWorkers(count int) {
	for i := 0; i < count; i++ {
		go func() {
			client := thttp.NewThriftHttpRpcClient(l.server)
			for {
				<-l.work
				l.sendOne(client)
			}
		}()
	}
}

func main() {
	server := flag.String("server", "localhost:9999", "URL of hfile server")
	collection := flag.String("collection", "testdata", "name of collection")
	graphite := report.Flag()
	workers := flag.Int("workers", 8, "worker pool size")
	qps := flag.Int("qps", 100, "qps to attempt")
	sample := flag.Int64("sampleSize", 1000, "number of random keys to use")

	flag.Parse()

	if !strings.Contains(*server, "/") {
		fmt.Println("URL doens't appear to specify a path -- appending /rpc/HFileService")
		*server = *server + "/rpc/HFileService"
	}

	if !strings.HasPrefix(*server, "http") {
		*server = "http://" + *server
	}

	r := report.NewRecorder().
		MaybeReportTo(graphite).
		LogToConsole(time.Second * 10).
		SetAsDefault()

	l := &Load{
		collection: *collection,
		sample:     sample,
		server:     *server,
		work:       make(chan bool, (*qps)*(*workers)),
		dropped:    r.GetMeter("dropped"),
		queueSize:  r.GetGuage("queue"),
		rtt:        r.GetTimer("rtt"),
	}

	if err := l.setKeys(); err != nil {
		fmt.Println("Failed to fetch testing keys:", err)
		os.Exit(1)
	}

	fmt.Printf("Sending %dqps to %s, drawing from %d random keys...\n", *qps, *server, len(l.keys))

	l.startWorkers(*workers)
	l.generator(*qps)

}
<|endoftext|>