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

package main

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"testing"
	"time"
	"unicode/utf8"
)

func TestExactly16Bytes(t *testing.T) {
	var tests = []string{
		"",
		"a",
		"日本語",
		"1234567890123456",
		"12345678901234567890",
		"1234567890123本語4567890",
		"12345678901234日本語567890",
		"123456789012345日本語67890",
		"1234567890123456日本語7890",
		"1234567890123456日本語7日本語890",
	}
	for _, str := range tests {
		got := exactly16Bytes(str)
		if len(got) != 16 {
			t.Errorf("exactly16Bytes(%q) is %q, length %d", str, got, len(got))
		}
		// Make sure it is full runes.
		for _, c := range got {
			if c == utf8.RuneError {
				t.Errorf("exactly16Bytes(%q) is %q, has partial rune", str, got)
			}
		}
	}
}

// tmpDir creates a temporary directory and returns its name.
func tmpDir(t *testing.T) string {
	name, err := ioutil.TempDir("", "pack")
	if err != nil {
		t.Fatal(err)
	}
	return name
}

// Test that we can create an archive, write to it, and get the same contents back.
// Tests the rv and then the pv command on a new archive.
func TestCreate(t *testing.T) {
	dir := tmpDir(t)
	defer os.RemoveAll(dir)
	name := filepath.Join(dir, "pack.a")
	ar := archive(name, os.O_RDWR, nil)
	// Add an entry by hand.
	ar.addFile(helloFile.Reset())
	ar.fd.Close()
	// Now check it.
	ar = archive(name, os.O_RDONLY, []string{helloFile.name})
	var buf bytes.Buffer
	stdout = &buf
	verbose = true
	defer func() {
		stdout = os.Stdout
		verbose = false
	}()
	ar.scan(ar.printContents)
	ar.fd.Close()
	result := buf.String()
	// Expect verbose output plus file contents.
	expect := fmt.Sprintf("%s\n%s", helloFile.name, helloFile.contents)
	if result != expect {
		t.Fatalf("expected %q got %q", expect, result)
	}
}

// Test that we can create an archive, put some files in it, and get back a correct listing.
// Tests the tv command.
func TestTableOfContents(t *testing.T) {
	dir := tmpDir(t)
	defer os.RemoveAll(dir)
	name := filepath.Join(dir, "pack.a")
	ar := archive(name, os.O_RDWR, nil)

	// Add some entries by hand.
	ar.addFile(helloFile.Reset())
	ar.addFile(goodbyeFile.Reset())
	ar.fd.Close()

	// Now print it.
	ar = archive(name, os.O_RDONLY, nil)
	var buf bytes.Buffer
	stdout = &buf
	verbose = true
	defer func() {
		stdout = os.Stdout
		verbose = false
	}()
	ar.scan(ar.tableOfContents)
	ar.fd.Close()
	result := buf.String()
	// Expect verbose listing.
	expect := fmt.Sprintf("%s\n%s\n", helloFile.Entry(), goodbyeFile.Entry())
	if result != expect {
		t.Fatalf("expected %q got %q", expect, result)
	}

	// Do it again without verbose.
	verbose = false
	buf.Reset()
	ar = archive(name, os.O_RDONLY, nil)
	ar.scan(ar.tableOfContents)
	ar.fd.Close()
	result = buf.String()
	// Expect non-verbose listing.
	expect = fmt.Sprintf("%s\n%s\n", helloFile.name, goodbyeFile.name)
	if result != expect {
		t.Fatalf("expected %q got %q", expect, result)
	}

	// Do it again with file list arguments.
	verbose = false
	buf.Reset()
	ar = archive(name, os.O_RDONLY, []string{helloFile.name})
	ar.scan(ar.tableOfContents)
	ar.fd.Close()
	result = buf.String()
	// Expect only helloFile.
	expect = fmt.Sprintf("%s\n", helloFile.name)
	if result != expect {
		t.Fatalf("expected %q got %q", expect, result)
	}
}

// Test that we can create an archive, put some files in it, and get back a file.
// Tests the x command.
func TestExtract(t *testing.T) {
	dir := tmpDir(t)
	defer os.RemoveAll(dir)
	name := filepath.Join(dir, "pack.a")
	ar := archive(name, os.O_RDWR, nil)
	// Add some entries by hand.
	ar.addFile(helloFile.Reset())
	ar.addFile(goodbyeFile.Reset())
	ar.fd.Close()
	// Now extract one file. We chdir to the directory of the archive for simplicity.
	pwd, err := os.Getwd()
	if err != nil {
		t.Fatal("os.Getwd: ", err)
	}
	err = os.Chdir(dir)
	if err != nil {
		t.Fatal("os.Chdir: ", err)
	}
	defer func() {
		err := os.Chdir(pwd)
		if err != nil {
			t.Fatal("os.Chdir: ", err)
		}
	}()
	ar = archive(name, os.O_RDONLY, []string{goodbyeFile.name})
	ar.scan(ar.extractContents)
	ar.fd.Close()
	data, err := ioutil.ReadFile(goodbyeFile.name)
	if err != nil {
		t.Fatal(err)
	}
	// Expect contents of file.
	result := string(data)
	expect := goodbyeFile.contents
	if result != expect {
		t.Fatalf("expected %q got %q", expect, result)
	}
}

// Test that pack-created archives can be understood by the tools.
func TestHello(t *testing.T) {
	dir := tmpDir(t)
	defer os.RemoveAll(dir)
	hello := filepath.Join(dir, "hello.go")
	prog := `
		package main
		func main() {
			println("hello world")
		}
	`
	err := ioutil.WriteFile(hello, []byte(prog), 0666)
	if err != nil {
		t.Fatal(err)
	}

	char := findChar(t, dir)

	run := func(args ...string) string {
		return doRun(t, dir, args...)
	}

	run("go", "build", "cmd/pack") // writes pack binary to dir
	run("go", "tool", char+"g", "hello.go")
	run("./pack", "grc", "hello.a", "hello."+char)
	run("go", "tool", char+"l", "-o", "a.out", "hello.a")
	out := run("./a.out")
	if out != "hello world\n" {
		t.Fatal("incorrect output: %q, want %q", out, "hello world\n")
	}
}

// Test that pack works with very long lines in PKGDEF.
func TestLargeDefs(t *testing.T) {
	dir := tmpDir(t)
	defer os.RemoveAll(dir)
	large := filepath.Join(dir, "large.go")
	f, err := os.Create(large)
	if err != nil {
		t.Fatal(err)
	}

	printf := func(format string, args ...interface{}) {
		_, err := fmt.Fprintf(f, format, args...)
		if err != nil {
			t.Fatalf("Writing to %s: %v", large, err)
		}
	}

	printf("package large\n\ntype T struct {\n")
	for i := 0; i < 10000; i++ {
		printf("f%d int `tag:\"", i)
		for j := 0; j < 100; j++ {
			printf("t%d=%d,", j, j)
		}
		printf("\"`\n")
	}
	printf("}\n")
	if err = f.Close(); err != nil {
		t.Fatal(err)
	}

	main := filepath.Join(dir, "main.go")
	prog := `
		package main
		import "./large"
		var V large.T
		func main() {
			println("ok")
		}
	`
	err = ioutil.WriteFile(main, []byte(prog), 0666)
	if err != nil {
		t.Fatal(err)
	}

	char := findChar(t, dir)

	run := func(args ...string) string {
		return doRun(t, dir, args...)
	}

	run("go", "build", "cmd/pack") // writes pack binary to dir
	run("go", "tool", char+"g", "large.go")
	run("./pack", "grc", "large.a", "large."+char)
	run("go", "tool", char+"g", "main.go")
	run("go", "tool", char+"l", "-o", "a.out", "main."+char)
	out := run("./a.out")
	if out != "ok\n" {
		t.Fatal("incorrect output: %q, want %q", out, "ok\n")
	}
}

// doRun runs a program in a directory and returns the output.
func doRun(t *testing.T, dir string, args ...string) string {
	cmd := exec.Command(args[0], args[1:]...)
	cmd.Dir = dir
	out, err := cmd.CombinedOutput()
	if err != nil {
		t.Fatalf("%v: %v\n%s", args, err, string(out))
	}
	return string(out)
}

// findChar returns the architecture character for the go command.
func findChar(t *testing.T, dir string) string {
	out := doRun(t, dir, "go", "env")
	re, err := regexp.Compile(`\s*GOCHAR=['"]?(\w)['"]?`)
	if err != nil {
		t.Fatal(err)
	}
	fields := re.FindStringSubmatch(out)
	if fields == nil {
		t.Fatal("cannot find GOCHAR in 'go env' output:\n", out)
	}
	return fields[1]
}

// Fake implementation of files.

var helloFile = &FakeFile{
	name:     "hello",
	contents: "hello world", // 11 bytes, an odd number.
	mode:     0644,
}

var goodbyeFile = &FakeFile{
	name:     "goodbye",
	contents: "Sayonara, Jim", // 13 bytes, another odd number.
	mode:     0644,
}

// FakeFile implements FileLike and also os.FileInfo.
type FakeFile struct {
	name     string
	contents string
	mode     os.FileMode
	offset   int
}

// Reset prepares a FakeFile for reuse.
func (f *FakeFile) Reset() *FakeFile {
	f.offset = 0
	return f
}

// FileLike methods.

func (f *FakeFile) Name() string {
	// A bit of a cheat: we only have a basename, so that's also ok for FileInfo.
	return f.name
}

func (f *FakeFile) Stat() (os.FileInfo, error) {
	return f, nil
}

func (f *FakeFile) Read(p []byte) (int, error) {
	if f.offset >= len(f.contents) {
		return 0, io.EOF
	}
	n := copy(p, f.contents[f.offset:])
	f.offset += n
	return n, nil
}

func (f *FakeFile) Close() error {
	return nil
}

// os.FileInfo methods.

func (f *FakeFile) Size() int64 {
	return int64(len(f.contents))
}

func (f *FakeFile) Mode() os.FileMode {
	return f.mode
}

func (f *FakeFile) ModTime() time.Time {
	return time.Time{}
}

func (f *FakeFile) IsDir() bool {
	return false
}

func (f *FakeFile) Sys() interface{} {
	return nil
}

// Special helpers.

func (f *FakeFile) Entry() *Entry {
	return &Entry{
		name:  f.name,
		mtime: 0, // Defined to be zero.
		uid:   0, // Ditto.
		gid:   0, // Ditto.
		mode:  f.mode,
		size:  int64(len(f.contents)),
	}
}
<commit_msg>cmd/pack: avoid ./ import in test (fix Windows build)<commit_after>// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"testing"
	"time"
	"unicode/utf8"
)

func TestExactly16Bytes(t *testing.T) {
	var tests = []string{
		"",
		"a",
		"日本語",
		"1234567890123456",
		"12345678901234567890",
		"1234567890123本語4567890",
		"12345678901234日本語567890",
		"123456789012345日本語67890",
		"1234567890123456日本語7890",
		"1234567890123456日本語7日本語890",
	}
	for _, str := range tests {
		got := exactly16Bytes(str)
		if len(got) != 16 {
			t.Errorf("exactly16Bytes(%q) is %q, length %d", str, got, len(got))
		}
		// Make sure it is full runes.
		for _, c := range got {
			if c == utf8.RuneError {
				t.Errorf("exactly16Bytes(%q) is %q, has partial rune", str, got)
			}
		}
	}
}

// tmpDir creates a temporary directory and returns its name.
func tmpDir(t *testing.T) string {
	name, err := ioutil.TempDir("", "pack")
	if err != nil {
		t.Fatal(err)
	}
	return name
}

// Test that we can create an archive, write to it, and get the same contents back.
// Tests the rv and then the pv command on a new archive.
func TestCreate(t *testing.T) {
	dir := tmpDir(t)
	defer os.RemoveAll(dir)
	name := filepath.Join(dir, "pack.a")
	ar := archive(name, os.O_RDWR, nil)
	// Add an entry by hand.
	ar.addFile(helloFile.Reset())
	ar.fd.Close()
	// Now check it.
	ar = archive(name, os.O_RDONLY, []string{helloFile.name})
	var buf bytes.Buffer
	stdout = &buf
	verbose = true
	defer func() {
		stdout = os.Stdout
		verbose = false
	}()
	ar.scan(ar.printContents)
	ar.fd.Close()
	result := buf.String()
	// Expect verbose output plus file contents.
	expect := fmt.Sprintf("%s\n%s", helloFile.name, helloFile.contents)
	if result != expect {
		t.Fatalf("expected %q got %q", expect, result)
	}
}

// Test that we can create an archive, put some files in it, and get back a correct listing.
// Tests the tv command.
func TestTableOfContents(t *testing.T) {
	dir := tmpDir(t)
	defer os.RemoveAll(dir)
	name := filepath.Join(dir, "pack.a")
	ar := archive(name, os.O_RDWR, nil)

	// Add some entries by hand.
	ar.addFile(helloFile.Reset())
	ar.addFile(goodbyeFile.Reset())
	ar.fd.Close()

	// Now print it.
	ar = archive(name, os.O_RDONLY, nil)
	var buf bytes.Buffer
	stdout = &buf
	verbose = true
	defer func() {
		stdout = os.Stdout
		verbose = false
	}()
	ar.scan(ar.tableOfContents)
	ar.fd.Close()
	result := buf.String()
	// Expect verbose listing.
	expect := fmt.Sprintf("%s\n%s\n", helloFile.Entry(), goodbyeFile.Entry())
	if result != expect {
		t.Fatalf("expected %q got %q", expect, result)
	}

	// Do it again without verbose.
	verbose = false
	buf.Reset()
	ar = archive(name, os.O_RDONLY, nil)
	ar.scan(ar.tableOfContents)
	ar.fd.Close()
	result = buf.String()
	// Expect non-verbose listing.
	expect = fmt.Sprintf("%s\n%s\n", helloFile.name, goodbyeFile.name)
	if result != expect {
		t.Fatalf("expected %q got %q", expect, result)
	}

	// Do it again with file list arguments.
	verbose = false
	buf.Reset()
	ar = archive(name, os.O_RDONLY, []string{helloFile.name})
	ar.scan(ar.tableOfContents)
	ar.fd.Close()
	result = buf.String()
	// Expect only helloFile.
	expect = fmt.Sprintf("%s\n", helloFile.name)
	if result != expect {
		t.Fatalf("expected %q got %q", expect, result)
	}
}

// Test that we can create an archive, put some files in it, and get back a file.
// Tests the x command.
func TestExtract(t *testing.T) {
	dir := tmpDir(t)
	defer os.RemoveAll(dir)
	name := filepath.Join(dir, "pack.a")
	ar := archive(name, os.O_RDWR, nil)
	// Add some entries by hand.
	ar.addFile(helloFile.Reset())
	ar.addFile(goodbyeFile.Reset())
	ar.fd.Close()
	// Now extract one file. We chdir to the directory of the archive for simplicity.
	pwd, err := os.Getwd()
	if err != nil {
		t.Fatal("os.Getwd: ", err)
	}
	err = os.Chdir(dir)
	if err != nil {
		t.Fatal("os.Chdir: ", err)
	}
	defer func() {
		err := os.Chdir(pwd)
		if err != nil {
			t.Fatal("os.Chdir: ", err)
		}
	}()
	ar = archive(name, os.O_RDONLY, []string{goodbyeFile.name})
	ar.scan(ar.extractContents)
	ar.fd.Close()
	data, err := ioutil.ReadFile(goodbyeFile.name)
	if err != nil {
		t.Fatal(err)
	}
	// Expect contents of file.
	result := string(data)
	expect := goodbyeFile.contents
	if result != expect {
		t.Fatalf("expected %q got %q", expect, result)
	}
}

// Test that pack-created archives can be understood by the tools.
func TestHello(t *testing.T) {
	dir := tmpDir(t)
	defer os.RemoveAll(dir)
	hello := filepath.Join(dir, "hello.go")
	prog := `
		package main
		func main() {
			println("hello world")
		}
	`
	err := ioutil.WriteFile(hello, []byte(prog), 0666)
	if err != nil {
		t.Fatal(err)
	}

	char := findChar(t, dir)

	run := func(args ...string) string {
		return doRun(t, dir, args...)
	}

	run("go", "build", "cmd/pack") // writes pack binary to dir
	run("go", "tool", char+"g", "hello.go")
	run("./pack", "grc", "hello.a", "hello."+char)
	run("go", "tool", char+"l", "-o", "a.out", "hello.a")
	out := run("./a.out")
	if out != "hello world\n" {
		t.Fatal("incorrect output: %q, want %q", out, "hello world\n")
	}
}

// Test that pack works with very long lines in PKGDEF.
func TestLargeDefs(t *testing.T) {
	dir := tmpDir(t)
	defer os.RemoveAll(dir)
	large := filepath.Join(dir, "large.go")
	f, err := os.Create(large)
	if err != nil {
		t.Fatal(err)
	}

	printf := func(format string, args ...interface{}) {
		_, err := fmt.Fprintf(f, format, args...)
		if err != nil {
			t.Fatalf("Writing to %s: %v", large, err)
		}
	}

	printf("package large\n\ntype T struct {\n")
	for i := 0; i < 10000; i++ {
		printf("f%d int `tag:\"", i)
		for j := 0; j < 100; j++ {
			printf("t%d=%d,", j, j)
		}
		printf("\"`\n")
	}
	printf("}\n")
	if err = f.Close(); err != nil {
		t.Fatal(err)
	}

	main := filepath.Join(dir, "main.go")
	prog := `
		package main
		import "large"
		var V large.T
		func main() {
			println("ok")
		}
	`
	err = ioutil.WriteFile(main, []byte(prog), 0666)
	if err != nil {
		t.Fatal(err)
	}

	char := findChar(t, dir)

	run := func(args ...string) string {
		return doRun(t, dir, args...)
	}

	run("go", "build", "cmd/pack") // writes pack binary to dir
	run("go", "tool", char+"g", "large.go")
	run("./pack", "grc", "large.a", "large."+char)
	run("go", "tool", char+"g", "-I", ".", "main.go")
	run("go", "tool", char+"l", "-L", ".", "-o", "a.out", "main."+char)
	out := run("./a.out")
	if out != "ok\n" {
		t.Fatal("incorrect output: %q, want %q", out, "ok\n")
	}
}

// doRun runs a program in a directory and returns the output.
func doRun(t *testing.T, dir string, args ...string) string {
	cmd := exec.Command(args[0], args[1:]...)
	cmd.Dir = dir
	out, err := cmd.CombinedOutput()
	if err != nil {
		t.Fatalf("%v: %v\n%s", args, err, string(out))
	}
	return string(out)
}

// findChar returns the architecture character for the go command.
func findChar(t *testing.T, dir string) string {
	out := doRun(t, dir, "go", "env")
	re, err := regexp.Compile(`\s*GOCHAR=['"]?(\w)['"]?`)
	if err != nil {
		t.Fatal(err)
	}
	fields := re.FindStringSubmatch(out)
	if fields == nil {
		t.Fatal("cannot find GOCHAR in 'go env' output:\n", out)
	}
	return fields[1]
}

// Fake implementation of files.

var helloFile = &FakeFile{
	name:     "hello",
	contents: "hello world", // 11 bytes, an odd number.
	mode:     0644,
}

var goodbyeFile = &FakeFile{
	name:     "goodbye",
	contents: "Sayonara, Jim", // 13 bytes, another odd number.
	mode:     0644,
}

// FakeFile implements FileLike and also os.FileInfo.
type FakeFile struct {
	name     string
	contents string
	mode     os.FileMode
	offset   int
}

// Reset prepares a FakeFile for reuse.
func (f *FakeFile) Reset() *FakeFile {
	f.offset = 0
	return f
}

// FileLike methods.

func (f *FakeFile) Name() string {
	// A bit of a cheat: we only have a basename, so that's also ok for FileInfo.
	return f.name
}

func (f *FakeFile) Stat() (os.FileInfo, error) {
	return f, nil
}

func (f *FakeFile) Read(p []byte) (int, error) {
	if f.offset >= len(f.contents) {
		return 0, io.EOF
	}
	n := copy(p, f.contents[f.offset:])
	f.offset += n
	return n, nil
}

func (f *FakeFile) Close() error {
	return nil
}

// os.FileInfo methods.

func (f *FakeFile) Size() int64 {
	return int64(len(f.contents))
}

func (f *FakeFile) Mode() os.FileMode {
	return f.mode
}

func (f *FakeFile) ModTime() time.Time {
	return time.Time{}
}

func (f *FakeFile) IsDir() bool {
	return false
}

func (f *FakeFile) Sys() interface{} {
	return nil
}

// Special helpers.

func (f *FakeFile) Entry() *Entry {
	return &Entry{
		name:  f.name,
		mtime: 0, // Defined to be zero.
		uid:   0, // Ditto.
		gid:   0, // Ditto.
		mode:  f.mode,
		size:  int64(len(f.contents)),
	}
}
<|endoftext|>


<commit_before>package marathon

import (
	"encoding/json"
	"github.com/QubitProducts/bamboo/configuration"
	"io/ioutil"
	"net/http"
	"sort"
	"strings"
)

// Describes an app process running
type Task struct {
	Host string
	Port int
}

// An app may have multiple processes
type App struct {
	Id              string
	EscapedId       string
	HealthCheckPath string
	Tasks           []Task
	ServicePort     int
	Env             map[string]string
}

type AppList []App

func (slice AppList) Len() int {
	return len(slice)
}

func (slice AppList) Less(i, j int) bool {
	return slice[i].Id < slice[j].Id
}

func (slice AppList) Swap(i, j int) {
	slice[i], slice[j] = slice[j], slice[i]
}

type MarathonTaskList []MarathonTask

type MarathonTasks struct {
	Tasks MarathonTaskList `json:tasks`
}

type MarathonTask struct {
	AppId        string
	Id           string
	Host         string
	Ports        []int
	ServicePorts []int
	StartedAt    string
	StagedAt     string
	Version      string
}

func (slice MarathonTaskList) Len() int {
	return len(slice)
}

func (slice MarathonTaskList) Less(i, j int) bool {
	return slice[i].StagedAt < slice[j].StagedAt
}

func (slice MarathonTaskList) Swap(i, j int) {
	slice[i], slice[j] = slice[j], slice[i]
}

type MarathonApps struct {
	Apps []MarathonApp `json:apps`
}

type MarathonApp struct {
	Id           string            `json:id`
	HealthChecks []HealthChecks    `json:healthChecks`
	Ports        []int             `json:ports`
	Env          map[string]string `json:env`
}

type HealthChecks struct {
	Path string `json:path`
}

func fetchMarathonApps(endpoint string) (map[string]MarathonApp, error) {
	response, err := http.Get(endpoint + "/v2/apps")

	if err != nil {
		return nil, err
	} else {
		defer response.Body.Close()
		var appResponse MarathonApps

		contents, err := ioutil.ReadAll(response.Body)
		if err != nil {
			return nil, err
		}

		err = json.Unmarshal(contents, &appResponse)
		if err != nil {
			return nil, err
		}

		dataById := map[string]MarathonApp{}

		for _, appConfig := range appResponse.Apps {
			dataById[appConfig.Id] = appConfig
		}

		return dataById, nil
	}
}

func fetchTasks(endpoint string) (map[string][]MarathonTask, error) {
	client := &http.Client{}
	req, err := http.NewRequest("GET", endpoint+"/v2/tasks", nil)
	req.Header.Add("Accept", "application/json")
	response, err := client.Do(req)

	var tasks MarathonTasks

	if err != nil {
		return nil, err
	} else {
		contents, err := ioutil.ReadAll(response.Body)
		defer response.Body.Close()
		if err != nil {
			return nil, err
		}

		err = json.Unmarshal(contents, &tasks)
		if err != nil {
			return nil, err
		}

		taskList := tasks.Tasks
		sort.Sort(taskList)

		tasksById := map[string][]MarathonTask{}
		for _, task := range taskList {
			if tasksById[task.AppId] == nil {
				tasksById[task.AppId] = []MarathonTask{}
			}
			tasksById[task.AppId] = append(tasksById[task.AppId], task)
		}

		return tasksById, nil
	}
}

func createApps(tasksById map[string][]MarathonTask, marathonApps map[string]MarathonApp) AppList {

	apps := AppList{}

	for appId, tasks := range tasksById {
		simpleTasks := []Task{}

		for _, task := range tasks {
			if len(task.Ports) > 0 {
				simpleTasks = append(simpleTasks, Task{Host: task.Host, Port: task.Ports[0]})
			}
		}

		// Try to handle old app id format without slashes
		appPath := appId
		if !strings.HasPrefix(appId, "/") {
			appPath = "/" + appId
		}

		app := App{
			// Since Marathon 0.7, apps are namespaced with path
			Id: appPath,
			// Used for template
			EscapedId:       strings.Replace(appId, "/", "::", -1),
			Tasks:           simpleTasks,
			HealthCheckPath: parseHealthCheckPath(marathonApps[appId].HealthChecks),
			Env:             marathonApps[appId].Env,
		}

		if len(marathonApps[appId].Ports) > 0 {
			app.ServicePort = marathonApps[appId].Ports[0]
		}

		apps = append(apps, app)
	}
	return apps
}

func parseHealthCheckPath(checks []HealthChecks) string {
	if len(checks) > 0 {
		return checks[0].Path
	}
	return ""
}

/*
	Apps returns a struct that describes Marathon current app and their
	sub tasks information.

	Parameters:
		endpoint: Marathon HTTP endpoint, e.g. http://localhost:8080
*/
func FetchApps(maraconf configuration.Marathon) (AppList, error) {

	var applist AppList
	var err error

	// try all configured endpoints until one succeeds
	for _, url := range maraconf.Endpoints() {
		applist, err = _fetchApps(url)
		if err == nil {
			return applist, err
		}
	}
	// return last error
	return nil, err
}

func _fetchApps(url string) (AppList, error) {
	tasks, err := fetchTasks(url)
	if err != nil {
		return nil, err
	}

	marathonApps, err := fetchMarathonApps(url)
	if err != nil {
		return nil, err
	}

	apps := createApps(tasks, marathonApps)
	sort.Sort(apps)
	return apps, nil
}
<commit_msg>Add multiple ports to apps<commit_after>package marathon

import (
	"encoding/json"
	"github.com/QubitProducts/bamboo/configuration"
	"io/ioutil"
	"net/http"
	"sort"
	"strings"
)

// Describes an app process running
type Task struct {
	Host string
	Port int
}

// An app may have multiple processes
type App struct {
	Id              string
	EscapedId       string
	HealthCheckPath string
	Tasks           []Task
	ServicePorts    []int
	Env             map[string]string
}

type AppList []App

func (slice AppList) Len() int {
	return len(slice)
}

func (slice AppList) Less(i, j int) bool {
	return slice[i].Id < slice[j].Id
}

func (slice AppList) Swap(i, j int) {
	slice[i], slice[j] = slice[j], slice[i]
}

type MarathonTaskList []MarathonTask

type MarathonTasks struct {
	Tasks MarathonTaskList `json:tasks`
}

type MarathonTask struct {
	AppId        string
	Id           string
	Host         string
	Ports        []int
	ServicePorts []int
	StartedAt    string
	StagedAt     string
	Version      string
}

func (slice MarathonTaskList) Len() int {
	return len(slice)
}

func (slice MarathonTaskList) Less(i, j int) bool {
	return slice[i].StagedAt < slice[j].StagedAt
}

func (slice MarathonTaskList) Swap(i, j int) {
	slice[i], slice[j] = slice[j], slice[i]
}

type MarathonApps struct {
	Apps []MarathonApp `json:apps`
}

type MarathonApp struct {
	Id           string            `json:id`
	HealthChecks []HealthChecks    `json:healthChecks`
	Ports        []int             `json:ports`
	Env          map[string]string `json:env`
}

type HealthChecks struct {
	Path string `json:path`
}

func fetchMarathonApps(endpoint string) (map[string]MarathonApp, error) {
	response, err := http.Get(endpoint + "/v2/apps")

	if err != nil {
		return nil, err
	} else {
		defer response.Body.Close()
		var appResponse MarathonApps

		contents, err := ioutil.ReadAll(response.Body)
		if err != nil {
			return nil, err
		}

		err = json.Unmarshal(contents, &appResponse)
		if err != nil {
			return nil, err
		}

		dataById := map[string]MarathonApp{}

		for _, appConfig := range appResponse.Apps {
			dataById[appConfig.Id] = appConfig
		}

		return dataById, nil
	}
}

func fetchTasks(endpoint string) (map[string][]MarathonTask, error) {
	client := &http.Client{}
	req, err := http.NewRequest("GET", endpoint+"/v2/tasks", nil)
	req.Header.Add("Accept", "application/json")
	response, err := client.Do(req)

	var tasks MarathonTasks

	if err != nil {
		return nil, err
	} else {
		contents, err := ioutil.ReadAll(response.Body)
		defer response.Body.Close()
		if err != nil {
			return nil, err
		}

		err = json.Unmarshal(contents, &tasks)
		if err != nil {
			return nil, err
		}

		taskList := tasks.Tasks
		sort.Sort(taskList)

		tasksById := map[string][]MarathonTask{}
		for _, task := range taskList {
			if tasksById[task.AppId] == nil {
				tasksById[task.AppId] = []MarathonTask{}
			}
			tasksById[task.AppId] = append(tasksById[task.AppId], task)
		}

		return tasksById, nil
	}
}

func createApps(tasksById map[string][]MarathonTask, marathonApps map[string]MarathonApp) AppList {

	apps := AppList{}

	for appId, tasks := range tasksById {
		simpleTasks := []Task{}

		for _, task := range tasks {
			if len(task.Ports) > 0 {
				simpleTasks = append(simpleTasks, Task{Host: task.Host, Port: task.Ports[0]})
			}
		}

		// Try to handle old app id format without slashes
		appPath := appId
		if !strings.HasPrefix(appId, "/") {
			appPath = "/" + appId
		}

		app := App{
			// Since Marathon 0.7, apps are namespaced with path
			Id: appPath,
			// Used for template
			EscapedId:       strings.Replace(appId, "/", "::", -1),
			Tasks:           simpleTasks,
			HealthCheckPath: parseHealthCheckPath(marathonApps[appId].HealthChecks),
			Env:             marathonApps[appId].Env,
			ServicePorts:    marathonApps[appId].Ports,
		}

		apps = append(apps, app)
	}
	return apps
}

func parseHealthCheckPath(checks []HealthChecks) string {
	if len(checks) > 0 {
		return checks[0].Path
	}
	return ""
}

/*
	Apps returns a struct that describes Marathon current app and their
	sub tasks information.

	Parameters:
		endpoint: Marathon HTTP endpoint, e.g. http://localhost:8080
*/
func FetchApps(maraconf configuration.Marathon) (AppList, error) {

	var applist AppList
	var err error

	// try all configured endpoints until one succeeds
	for _, url := range maraconf.Endpoints() {
		applist, err = _fetchApps(url)
		if err == nil {
			return applist, err
		}
	}
	// return last error
	return nil, err
}

func _fetchApps(url string) (AppList, error) {
	tasks, err := fetchTasks(url)
	if err != nil {
		return nil, err
	}

	marathonApps, err := fetchMarathonApps(url)
	if err != nil {
		return nil, err
	}

	apps := createApps(tasks, marathonApps)
	sort.Sort(apps)
	return apps, nil
}
<|endoftext|>


<commit_before>package bot

import (
	"log"
	"strconv"
	"strings"
	"time"

	"github.com/belak/irc"
	"github.com/khades/servbot/commandHandlers"
	"github.com/khades/servbot/ircClient"
	"github.com/khades/servbot/models"
	"github.com/khades/servbot/repos"
)

var chatHandler irc.HandlerFunc = func(client *irc.Client, message *irc.Message) {
	msgID, found := message.Tags.GetTag("msg-id")
	if found {
		switch msgID {
		case "room_mods":
			{
				commaIndex := strings.Index(message.Params[1], ":")
				if commaIndex != -1 {
					mods := strings.Split(message.Params[1][commaIndex+2:], ", ")
					channel := message.Params[0][1:]
					modHandler(&channel, &mods)
				}
			}
		case "resub":
			{
				subHandler(message, &IrcClientInstance)
			}

		case "sub":
			{
				subHandler(message, &IrcClientInstance)
			}
		}
	}

	if message.Command == "CLEARCHAT" {
		banDuration, banDurationFound := message.Tags.GetTag("ban-duration")
		intBanDuration := 0
		if banDurationFound {
			parsedValue, parseError := strconv.Atoi(banDuration)
			if parseError == nil {
				intBanDuration = parsedValue
			}
		}
		banReason, _ := message.Tags.GetTag("ban-reason")
		user := message.Params[1]
		channel := message.Params[0]
		messageType := "timeout"
		if intBanDuration == 0 {
			messageType = "ban"
		}
		formedMessage := models.ChatMessage{
			MessageStruct: models.MessageStruct{
				Date:        time.Now(),
				MessageType: messageType,
				BanLength:   intBanDuration,
				BanReason:   banReason},
			Channel:   channel,
			ChannelID: message.Tags["room-id"].Encode(),
			User:      user,
			UserID:    message.Tags["target-user-id"].Encode()}
		repos.LogMessage(&formedMessage)
	}
	if message.Command == "PRIVMSG" {
		formedMessage := models.ChatMessage{
			MessageStruct: models.MessageStruct{
				Username:    message.User,
				MessageType: "message",
				MessageBody: message.Params[1],
				Date:        time.Now()},
			Channel:   message.Params[0][1:],
			ChannelID: message.Tags["room-id"].Encode(),
			User:      message.User,
			UserID:    message.Tags["user-id"].Encode(),
			IsMod:     message.Tags["mod"] == "1" || message.User == "khadesru" || message.Params[0][1:] == message.User,
			IsSub:     message.Tags["subscriber"] == "1",
			IsPrime:   strings.Contains(message.Tags["badges"].Encode(), "premium/1")}
		repos.LogMessage(&formedMessage)
		repos.DecrementAutoMessages(&formedMessage.ChannelID)
		commandBody, isCommand := formedMessage.GetCommand()
		bits, bitsFound := message.Tags.GetTag("bits")
		if bitsFound {
			parsedBits, parsedBitsError := strconv.Atoi(bits)
			if parsedBitsError == nil {
				repos.AddBitsToUser(&formedMessage.ChannelID, &formedMessage.UserID, &formedMessage.User, parsedBits, "bits")
			}
		}
		if isCommand {
			if message.User == "khadesru" && commandBody.Command == "debugSub" {
				subPlan := "2000"
				sendSubMessage(&formedMessage.Channel, &formedMessage.ChannelID, &formedMessage.User, &subPlan)
			}
			if message.User == "khadesru" && commandBody.Command == "debugResub" {
				resubCount := 3
				subPlan := "2000"
				sendResubMessage(&formedMessage.Channel, &formedMessage.ChannelID, &formedMessage.User, &resubCount, &subPlan)
			}
			handlerFunction := commandHandlers.Router.Go(commandBody.Command)
			handlerFunction(true, &formedMessage, commandBody, &IrcClientInstance)
		}
	}

	if message.Command == "001" {
		client.Write("CAP REQ twitch.tv/tags")
		client.Write("CAP REQ twitch.tv/membership")
		client.Write("CAP REQ twitch.tv/commands")
		for _, value := range repos.Config.Channels {
			client.Write("JOIN #" + value)
		}
		IrcClientInstance = ircClient.IrcClient{Client: client, Bounces: make(map[string]time.Time), Ready: true}
		IrcClientInstance.SendModsCommand()
		log.Println("Bot is started")
	}
}
<commit_msg>Debug: Getting debug messages from jtv\tmi<commit_after>package bot

import (
	"log"
	"strconv"
	"strings"
	"time"

	"github.com/belak/irc"
	"github.com/khades/servbot/commandHandlers"
	"github.com/khades/servbot/ircClient"
	"github.com/khades/servbot/models"
	"github.com/khades/servbot/repos"
)

var chatHandler irc.HandlerFunc = func(client *irc.Client, message *irc.Message) {
	if strings.Contains(message.String(), ":jtv") {
		log.Println("JTV: " + message.String())
	}
	if strings.Contains(message.String(), ":tmi.twitch.tv") {
		log.Println("TMI.TWITCH.TV: " + message.String())
	}
	msgID, found := message.Tags.GetTag("msg-id")
	if found {
		switch msgID {
		case "room_mods":
			{
				commaIndex := strings.Index(message.Params[1], ":")
				if commaIndex != -1 {
					mods := strings.Split(message.Params[1][commaIndex+2:], ", ")
					channel := message.Params[0][1:]
					modHandler(&channel, &mods)
				}
			}
		case "resub":
			{
				subHandler(message, &IrcClientInstance)
			}

		case "sub":
			{
				subHandler(message, &IrcClientInstance)
			}
		}
	}

	if message.Command == "CLEARCHAT" {
		banDuration, banDurationFound := message.Tags.GetTag("ban-duration")
		intBanDuration := 0
		if banDurationFound {
			parsedValue, parseError := strconv.Atoi(banDuration)
			if parseError == nil {
				intBanDuration = parsedValue
			}
		}
		banReason, _ := message.Tags.GetTag("ban-reason")
		user := message.Params[1]
		channel := message.Params[0]
		messageType := "timeout"
		if intBanDuration == 0 {
			messageType = "ban"
		}
		formedMessage := models.ChatMessage{
			MessageStruct: models.MessageStruct{
				Date:        time.Now(),
				MessageType: messageType,
				BanLength:   intBanDuration,
				BanReason:   banReason},
			Channel:   channel,
			ChannelID: message.Tags["room-id"].Encode(),
			User:      user,
			UserID:    message.Tags["target-user-id"].Encode()}
		repos.LogMessage(&formedMessage)
	}
	if message.Command == "PRIVMSG" {
		formedMessage := models.ChatMessage{
			MessageStruct: models.MessageStruct{
				Username:    message.User,
				MessageType: "message",
				MessageBody: message.Params[1],
				Date:        time.Now()},
			Channel:   message.Params[0][1:],
			ChannelID: message.Tags["room-id"].Encode(),
			User:      message.User,
			UserID:    message.Tags["user-id"].Encode(),
			IsMod:     message.Tags["mod"] == "1" || message.User == "khadesru" || message.Params[0][1:] == message.User,
			IsSub:     message.Tags["subscriber"] == "1",
			IsPrime:   strings.Contains(message.Tags["badges"].Encode(), "premium/1")}
		repos.LogMessage(&formedMessage)
		repos.DecrementAutoMessages(&formedMessage.ChannelID)
		commandBody, isCommand := formedMessage.GetCommand()
		bits, bitsFound := message.Tags.GetTag("bits")
		if bitsFound {
			parsedBits, parsedBitsError := strconv.Atoi(bits)
			if parsedBitsError == nil {
				repos.AddBitsToUser(&formedMessage.ChannelID, &formedMessage.UserID, &formedMessage.User, parsedBits, "bits")
			}
		}
		if isCommand {
			if message.User == "khadesru" && commandBody.Command == "debugSub" {
				subPlan := "2000"
				sendSubMessage(&formedMessage.Channel, &formedMessage.ChannelID, &formedMessage.User, &subPlan)
			}
			if message.User == "khadesru" && commandBody.Command == "debugResub" {
				resubCount := 3
				subPlan := "2000"
				sendResubMessage(&formedMessage.Channel, &formedMessage.ChannelID, &formedMessage.User, &resubCount, &subPlan)
			}
			handlerFunction := commandHandlers.Router.Go(commandBody.Command)
			handlerFunction(true, &formedMessage, commandBody, &IrcClientInstance)
		}
	}

	if message.Command == "001" {
		client.Write("CAP REQ twitch.tv/tags")
		client.Write("CAP REQ twitch.tv/membership")
		client.Write("CAP REQ twitch.tv/commands")
		for _, value := range repos.Config.Channels {
			client.Write("JOIN #" + value)
		}
		IrcClientInstance = ircClient.IrcClient{Client: client, Bounces: make(map[string]time.Time), Ready: true}
		IrcClientInstance.SendModsCommand()
		log.Println("Bot is started")
	}
}
<|endoftext|>


<commit_before>// Copyright (c) 2013-2014 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package btcchain

import (
	"fmt"
	"github.com/conformal/btcscript"
	"github.com/conformal/btcutil"
	"github.com/conformal/btcwire"
	"math"
	"runtime"
)

// txValidateItem holds a transaction along with which input to validate.
type txValidateItem struct {
	txInIndex int
	txIn      *btcwire.TxIn
	tx        *btcutil.Tx
}

// txValidator provides a type which asynchronously validates transaction
// inputs.  It provides several channels for communication and a processing
// function that is intended to be in run multiple goroutines.
type txValidator struct {
	validateChan chan *txValidateItem
	quitChan     chan bool
	resultChan   chan error
	txStore      TxStore
	flags        btcscript.ScriptFlags
}

// sendResult sends the result of a script pair validation on the internal
// result channel while respecting the quit channel.  The allows orderly
// shutdown when the validation process is aborted early due to a validation
// error in one of the other goroutines.
func (v *txValidator) sendResult(result error) {
	select {
	case v.resultChan <- result:
	case <-v.quitChan:
	}
}

// validateHandler consumes items to validate from the internal validate channel
// and returns the result of the validation on the internal result channel. It
// must be run as a goroutine.
func (v *txValidator) validateHandler() {
out:
	for {
		select {
		case txVI := <-v.validateChan:
			// Ensure the referenced input transaction is available.
			txIn := txVI.txIn
			originTxHash := &txIn.PreviousOutpoint.Hash
			originTx, exists := v.txStore[*originTxHash]
			if !exists || originTx.Err != nil || originTx.Tx == nil {
				str := fmt.Sprintf("unable to find input "+
					"transaction %v referenced from "+
					"transaction %v", originTxHash,
					txVI.tx.Sha())
				err := ruleError(ErrMissingTx, str)
				v.sendResult(err)
				break out
			}
			originMsgTx := originTx.Tx.MsgTx()

			// Ensure the output index in the referenced transaction
			// is available.
			originTxIndex := txIn.PreviousOutpoint.Index
			if originTxIndex >= uint32(len(originMsgTx.TxOut)) {
				str := fmt.Sprintf("out of bounds "+
					"input index %d in transaction %v "+
					"referenced from transaction %v",
					originTxIndex, originTxHash,
					txVI.tx.Sha())
				err := ruleError(ErrBadTxInput, str)
				v.sendResult(err)
				break out
			}

			// Create a new script engine for the script pair.
			sigScript := txIn.SignatureScript
			pkScript := originMsgTx.TxOut[originTxIndex].PkScript
			engine, err := btcscript.NewScript(sigScript, pkScript,
				txVI.txInIndex, txVI.tx.MsgTx(), v.flags)
			if err != nil {
				str := fmt.Sprintf("failed to parse input "+
					"%s:%d which references output %s:%d - "+
					"%v (input script bytes %x, prev output "+
					"script bytes %x)", txVI.tx.Sha(),
					txVI.txInIndex, originTxHash,
					originTxIndex, err, sigScript, pkScript)
				err := ruleError(ErrScriptMalformed, str)
				v.sendResult(err)
				break out
			}

			// Execute the script pair.
			if err := engine.Execute(); err != nil {
				str := fmt.Sprintf("failed to validate input "+
					"%s:%d which references output %s:%d - "+
					"%v (input script bytes %x, prev output "+
					"script bytes %x)", txVI.tx.Sha(),
					txVI.txInIndex, originTxHash,
					originTxIndex, err, sigScript, pkScript)
				err := ruleError(ErrScriptValidation, str)
				v.sendResult(err)
				break out
			}

			// Validation succeeded.
			v.sendResult(nil)

		case <-v.quitChan:
			break out
		}
	}
}

// Validate validates the scripts for all of the passed transaction inputs using
// multiple goroutines.
func (v *txValidator) Validate(items []*txValidateItem) error {
	if len(items) == 0 {
		return nil
	}

	// Limit the number of goroutines to do script validation based on the
	// number of processor cores.  This help ensure the system stays
	// reasonably responsive under heavy load.
	maxGoRoutines := runtime.NumCPU() * 3
	if maxGoRoutines <= 0 {
		maxGoRoutines = 1
	}
	if maxGoRoutines > len(items) {
		maxGoRoutines = len(items)
	}

	// Start up validation handlers that are used to asynchronously
	// validate each transaction input.
	for i := 0; i < maxGoRoutines; i++ {
		go v.validateHandler()
	}

	// Validate each of the inputs.  The quit channel is closed when any
	// errors occur so all processing goroutines exit regardless of which
	// input had the validation error.
	numInputs := len(items)
	currentItem := 0
	processedItems := 0
	for processedItems < numInputs {
		// Only send items while there are still items that need to
		// be processed.  The select statement will never select a nil
		// channel.
		var validateChan chan *txValidateItem
		var item *txValidateItem
		if currentItem < numInputs {
			validateChan = v.validateChan
			item = items[currentItem]
		}

		select {
		case validateChan <- item:
			currentItem++

		case err := <-v.resultChan:
			processedItems++
			if err != nil {
				close(v.quitChan)
				return err
			}
		}
	}

	close(v.quitChan)
	return nil
}

// newTxValidator returns a new instance of txValidator to be used for
// validating transaction scripts asynchronously.
func newTxValidator(txStore TxStore, flags btcscript.ScriptFlags) *txValidator {
	return &txValidator{
		validateChan: make(chan *txValidateItem),
		quitChan:     make(chan bool),
		resultChan:   make(chan error),
		txStore:      txStore,
		flags:        flags,
	}
}

// ValidateTransactionScripts validates the scripts for the passed transaction
// using multiple goroutines.
func ValidateTransactionScripts(tx *btcutil.Tx, txStore TxStore, flags btcscript.ScriptFlags) error {
	// Collect all of the transaction inputs and required information for
	// validation.
	txIns := tx.MsgTx().TxIn
	txValItems := make([]*txValidateItem, 0, len(txIns))
	for txInIdx, txIn := range txIns {
		// Skip coinbases.
		if txIn.PreviousOutpoint.Index == math.MaxUint32 {
			continue
		}

		txVI := &txValidateItem{
			txInIndex: txInIdx,
			txIn:      txIn,
			tx:        tx,
		}
		txValItems = append(txValItems, txVI)
	}

	// Validate all of the inputs.
	validator := newTxValidator(txStore, flags)
	if err := validator.Validate(txValItems); err != nil {
		return err
	}

	return nil

}

// checkBlockScripts executes and validates the scripts for all transactions in
// the passed block.
func checkBlockScripts(block *btcutil.Block, txStore TxStore) error {
	// Setup the script validation flags.  Blocks created after the BIP0016
	// activation time need to have the pay-to-script-hash checks enabled.
	var flags btcscript.ScriptFlags
	if block.MsgBlock().Header.Timestamp.After(btcscript.Bip16Activation) {
		flags |= btcscript.ScriptBip16
	}

	// Collect all of the transaction inputs and required information for
	// validation for all transactions in the block into a single slice.
	numInputs := 0
	for _, tx := range block.Transactions() {
		numInputs += len(tx.MsgTx().TxIn)
	}
	txValItems := make([]*txValidateItem, 0, numInputs)
	for _, tx := range block.Transactions() {
		for txInIdx, txIn := range tx.MsgTx().TxIn {
			// Skip coinbases.
			if txIn.PreviousOutpoint.Index == math.MaxUint32 {
				continue
			}

			txVI := &txValidateItem{
				txInIndex: txInIdx,
				txIn:      txIn,
				tx:        tx,
			}
			txValItems = append(txValItems, txVI)
		}
	}

	// Validate all of the inputs.
	validator := newTxValidator(txStore, flags)
	if err := validator.Validate(txValItems); err != nil {
		return err
	}

	return nil
}
<commit_msg>Use chan struct{} for semaphores<commit_after>// Copyright (c) 2013-2014 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package btcchain

import (
	"fmt"
	"github.com/conformal/btcscript"
	"github.com/conformal/btcutil"
	"github.com/conformal/btcwire"
	"math"
	"runtime"
)

// txValidateItem holds a transaction along with which input to validate.
type txValidateItem struct {
	txInIndex int
	txIn      *btcwire.TxIn
	tx        *btcutil.Tx
}

// txValidator provides a type which asynchronously validates transaction
// inputs.  It provides several channels for communication and a processing
// function that is intended to be in run multiple goroutines.
type txValidator struct {
	validateChan chan *txValidateItem
	quitChan     chan struct{}
	resultChan   chan error
	txStore      TxStore
	flags        btcscript.ScriptFlags
}

// sendResult sends the result of a script pair validation on the internal
// result channel while respecting the quit channel.  The allows orderly
// shutdown when the validation process is aborted early due to a validation
// error in one of the other goroutines.
func (v *txValidator) sendResult(result error) {
	select {
	case v.resultChan <- result:
	case <-v.quitChan:
	}
}

// validateHandler consumes items to validate from the internal validate channel
// and returns the result of the validation on the internal result channel. It
// must be run as a goroutine.
func (v *txValidator) validateHandler() {
out:
	for {
		select {
		case txVI := <-v.validateChan:
			// Ensure the referenced input transaction is available.
			txIn := txVI.txIn
			originTxHash := &txIn.PreviousOutpoint.Hash
			originTx, exists := v.txStore[*originTxHash]
			if !exists || originTx.Err != nil || originTx.Tx == nil {
				str := fmt.Sprintf("unable to find input "+
					"transaction %v referenced from "+
					"transaction %v", originTxHash,
					txVI.tx.Sha())
				err := ruleError(ErrMissingTx, str)
				v.sendResult(err)
				break out
			}
			originMsgTx := originTx.Tx.MsgTx()

			// Ensure the output index in the referenced transaction
			// is available.
			originTxIndex := txIn.PreviousOutpoint.Index
			if originTxIndex >= uint32(len(originMsgTx.TxOut)) {
				str := fmt.Sprintf("out of bounds "+
					"input index %d in transaction %v "+
					"referenced from transaction %v",
					originTxIndex, originTxHash,
					txVI.tx.Sha())
				err := ruleError(ErrBadTxInput, str)
				v.sendResult(err)
				break out
			}

			// Create a new script engine for the script pair.
			sigScript := txIn.SignatureScript
			pkScript := originMsgTx.TxOut[originTxIndex].PkScript
			engine, err := btcscript.NewScript(sigScript, pkScript,
				txVI.txInIndex, txVI.tx.MsgTx(), v.flags)
			if err != nil {
				str := fmt.Sprintf("failed to parse input "+
					"%s:%d which references output %s:%d - "+
					"%v (input script bytes %x, prev output "+
					"script bytes %x)", txVI.tx.Sha(),
					txVI.txInIndex, originTxHash,
					originTxIndex, err, sigScript, pkScript)
				err := ruleError(ErrScriptMalformed, str)
				v.sendResult(err)
				break out
			}

			// Execute the script pair.
			if err := engine.Execute(); err != nil {
				str := fmt.Sprintf("failed to validate input "+
					"%s:%d which references output %s:%d - "+
					"%v (input script bytes %x, prev output "+
					"script bytes %x)", txVI.tx.Sha(),
					txVI.txInIndex, originTxHash,
					originTxIndex, err, sigScript, pkScript)
				err := ruleError(ErrScriptValidation, str)
				v.sendResult(err)
				break out
			}

			// Validation succeeded.
			v.sendResult(nil)

		case <-v.quitChan:
			break out
		}
	}
}

// Validate validates the scripts for all of the passed transaction inputs using
// multiple goroutines.
func (v *txValidator) Validate(items []*txValidateItem) error {
	if len(items) == 0 {
		return nil
	}

	// Limit the number of goroutines to do script validation based on the
	// number of processor cores.  This help ensure the system stays
	// reasonably responsive under heavy load.
	maxGoRoutines := runtime.NumCPU() * 3
	if maxGoRoutines <= 0 {
		maxGoRoutines = 1
	}
	if maxGoRoutines > len(items) {
		maxGoRoutines = len(items)
	}

	// Start up validation handlers that are used to asynchronously
	// validate each transaction input.
	for i := 0; i < maxGoRoutines; i++ {
		go v.validateHandler()
	}

	// Validate each of the inputs.  The quit channel is closed when any
	// errors occur so all processing goroutines exit regardless of which
	// input had the validation error.
	numInputs := len(items)
	currentItem := 0
	processedItems := 0
	for processedItems < numInputs {
		// Only send items while there are still items that need to
		// be processed.  The select statement will never select a nil
		// channel.
		var validateChan chan *txValidateItem
		var item *txValidateItem
		if currentItem < numInputs {
			validateChan = v.validateChan
			item = items[currentItem]
		}

		select {
		case validateChan <- item:
			currentItem++

		case err := <-v.resultChan:
			processedItems++
			if err != nil {
				close(v.quitChan)
				return err
			}
		}
	}

	close(v.quitChan)
	return nil
}

// newTxValidator returns a new instance of txValidator to be used for
// validating transaction scripts asynchronously.
func newTxValidator(txStore TxStore, flags btcscript.ScriptFlags) *txValidator {
	return &txValidator{
		validateChan: make(chan *txValidateItem),
		quitChan:     make(chan struct{}),
		resultChan:   make(chan error),
		txStore:      txStore,
		flags:        flags,
	}
}

// ValidateTransactionScripts validates the scripts for the passed transaction
// using multiple goroutines.
func ValidateTransactionScripts(tx *btcutil.Tx, txStore TxStore, flags btcscript.ScriptFlags) error {
	// Collect all of the transaction inputs and required information for
	// validation.
	txIns := tx.MsgTx().TxIn
	txValItems := make([]*txValidateItem, 0, len(txIns))
	for txInIdx, txIn := range txIns {
		// Skip coinbases.
		if txIn.PreviousOutpoint.Index == math.MaxUint32 {
			continue
		}

		txVI := &txValidateItem{
			txInIndex: txInIdx,
			txIn:      txIn,
			tx:        tx,
		}
		txValItems = append(txValItems, txVI)
	}

	// Validate all of the inputs.
	validator := newTxValidator(txStore, flags)
	if err := validator.Validate(txValItems); err != nil {
		return err
	}

	return nil

}

// checkBlockScripts executes and validates the scripts for all transactions in
// the passed block.
func checkBlockScripts(block *btcutil.Block, txStore TxStore) error {
	// Setup the script validation flags.  Blocks created after the BIP0016
	// activation time need to have the pay-to-script-hash checks enabled.
	var flags btcscript.ScriptFlags
	if block.MsgBlock().Header.Timestamp.After(btcscript.Bip16Activation) {
		flags |= btcscript.ScriptBip16
	}

	// Collect all of the transaction inputs and required information for
	// validation for all transactions in the block into a single slice.
	numInputs := 0
	for _, tx := range block.Transactions() {
		numInputs += len(tx.MsgTx().TxIn)
	}
	txValItems := make([]*txValidateItem, 0, numInputs)
	for _, tx := range block.Transactions() {
		for txInIdx, txIn := range tx.MsgTx().TxIn {
			// Skip coinbases.
			if txIn.PreviousOutpoint.Index == math.MaxUint32 {
				continue
			}

			txVI := &txValidateItem{
				txInIndex: txInIdx,
				txIn:      txIn,
				tx:        tx,
			}
			txValItems = append(txValItems, txVI)
		}
	}

	// Validate all of the inputs.
	validator := newTxValidator(txStore, flags)
	if err := validator.Validate(txValItems); err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"time"

	"github.com/microo8/golymer"
)

//FancyClock custom element that shows the current time
type FancyClock struct {
	golymer.Element
	time   string
	Format string
	ctx    context.Context
	cancel context.CancelFunc
}

//ConnectedCallback when the element is connected to the DOM, ticking may begin!
func (ce *FancyClock) ConnectedCallback() {
	ce.Element.ConnectedCallback() //must call this first
	//starts the ticking by spinning up a goroutine
	go func() {
		for {
			select {
			case <-ce.ctx.Done():
				//stops the thicking goroutine
				return
			case <-time.Tick(time.Second):
				//updates current time with the format
				ce.time = time.Now().Format(ce.Format)
			}
		}
	}()
}

//DisconnectedCallback when the element is removed from the DOM
//it stops the ticking by sending done to the context
func (ce *FancyClock) DisconnectedCallback() {
	ce.cancel()
}

//NewClockElem creates new clock-elem element
func NewClockElem() *FancyClock {
	ce := &FancyClock{Format: time.UnixDate}
	ce.ctx, ce.cancel = context.WithCancel(context.Background())
	ce.Template = `
	<style>
		:host {
			display: inline;
		    box-shadow: 0 0.018em 0.05em 0;
			font-size: 7rem;
		}
	</style>
	[[time]]`
	return ce
}

func main() {
	//define the new fancy-clock elem
	err := golymer.Define(NewClockElem)
	if err != nil {
		panic(err)
	}
}
<commit_msg>Fix clock example to the new Template style<commit_after>package main

import (
	"time"

	"github.com/microo8/golymer"
)

var clockTemplate = golymer.NewTemplate(`
<style>
	:host {
		display: inline;
		box-shadow: 0 0.018em 0.05em 0;
		font-size: 7rem;
	}
</style>
[[time]]`)

//FancyClock custom element that shows the current time
type FancyClock struct {
	golymer.Element
	time   string
	Format string
	done   chan struct{}
}

//ConnectedCallback when the element is connected to the DOM, ticking may begin!
func (ce *FancyClock) ConnectedCallback() {
	ce.Element.ConnectedCallback() //must call this first
	//starts the ticking by spinning up a goroutine
	go func() {
		for {
			select {
			case <-ce.done:
				//stops the thicking goroutine
				return
			case <-time.Tick(time.Second):
				//updates current time with the format
				ce.time = time.Now().Format(ce.Format)
			}
		}
	}()
}

//DisconnectedCallback when the element is removed from the DOM
//it stops the ticking by sending done to the context
func (ce *FancyClock) DisconnectedCallback() {
	ce.done <- struct{}{}
}

//NewClockElem creates new clock-elem element
func NewClockElem() *FancyClock {
	ce := &FancyClock{Format: time.UnixDate}
	ce.done = make(chan struct{})
	ce.SetTemplate(clockTemplate)
	return ce
}

func main() {
	//define the new fancy-clock elem
	err := golymer.Define(NewClockElem)
	if err != nil {
		panic(err)
	}
}
<|endoftext|>


<commit_before>package chronograf

import (
	"context"
	"net/http"
	"time"
)

// General errors.
const (
	ErrUpstreamTimeout     = Error("request to backend timed out")
	ErrExplorationNotFound = Error("exploration not found")
	ErrSourceNotFound      = Error("source not found")
	ErrServerNotFound      = Error("server not found")
	ErrLayoutNotFound      = Error("layout not found")
	ErrUserNotFound        = Error("user not found")
	ErrLayoutInvalid       = Error("layout is invalid")
	ErrAlertNotFound       = Error("alert not found")
	ErrAuthentication      = Error("user not authenticated")
)

// Error is a domain error encountered while processing chronograf requests
type Error string

func (e Error) Error() string {
	return string(e)
}

// Logger represents an abstracted structured logging implementation. It
// provides methods to trigger log messages at various alert levels and a
// WithField method to set keys for a structured log message.
type Logger interface {
	Debug(...interface{})
	Info(...interface{})
	Warn(...interface{})
	Error(...interface{})
	Fatal(...interface{})
	Panic(...interface{})

	WithField(string, interface{}) Logger
}

// Assets returns a handler to serve the website.
type Assets interface {
	Handler() http.Handler
}

// TimeSeries represents a queryable time series database.
type TimeSeries interface {
	// Query retrieves time series data from the database.
	Query(context.Context, Query) (Response, error)
	// Connect will connect to the time series using the information in `Source`.
	Connect(context.Context, *Source) error
}

// Range represents an upper and lower bound for data
type Range struct {
	Upper int64 `json:"upper,omitempty"` // Upper is the upper bound
	Lower int64 `json:"lower,omitempty"` // Lower is the lower bound
}

// Query retrieves a Response from a TimeSeries.
type Query struct {
	Command  string   `json:"query"`              // Command is the query itself
	DB       string   `json:"db,omitempty"`       // DB is optional and if empty will not be used.
	RP       string   `json:"rp,omitempty"`       // RP is a retention policy and optional; if empty will not be used.
	Wheres   []string `json:"wheres,omitempty"`   // Wheres restricts the query to certain attributes
	GroupBys []string `json:"groupbys,omitempty"` // GroupBys collate the query by these tags
	Label    string   `json:"label,omitempty"`    // Label is the Y-Axis label for the data
	Range    *Range   `json:"range,omitempty"`    // Range is the default Y-Axis range for the data
}

// Response is the result of a query against a TimeSeries
type Response interface {
	MarshalJSON() ([]byte, error)
}

// Source is connection information to a time-series data store.
type Source struct {
	ID       int    `json:"id,omitempty,string"` // ID is the unique ID of the source
	Name     string `json:"name"`                // Name is the user-defined name for the source
	Type     string `json:"type,omitempty"`      // Type specifies which kinds of source (enterprise vs oss)
	Username string `json:"username,omitempty"`  // Username is the username to connect to the source
	Password string `json:"password,omitempty"`  // Password is in CLEARTEXT
	URL      string `json:"url"`                 // URL are the connections to the source
	Default  bool   `json:"default"`             // Default specifies the default source for the application
	Telegraf string `json:"telegraf"`            // Telegraf is the db telegraf is written to.  By default it is "telegraf"
}

// SourcesStore stores connection information for a `TimeSeries`
type SourcesStore interface {
	// All returns all sources in the store
	All(context.Context) ([]Source, error)
	// Add creates a new source in the SourcesStore and returns Source with ID
	Add(context.Context, Source) (Source, error)
	// Delete the Source from the store
	Delete(context.Context, Source) error
	// Get retrieves Source if `ID` exists
	Get(ctx context.Context, ID int) (Source, error)
	// Update the Source in the store.
	Update(context.Context, Source) error
}

// AlertRule represents rules for building a tickscript alerting task
type AlertRule struct {
	ID            string        `json:"id,omitempty"` // ID is the unique ID of the alert
	Query         QueryConfig   `json:"query"`        // Query is the filter of data for the alert.
	Every         string        `json:"every"`        // Every how often to check for the alerting criteria
	Alerts        []string      `json:"alerts"`       // AlertServices name all the services to notify (e.g. pagerduty)
	Message       string        `json:"message"`      // Message included with alert
	Trigger       string        `json:"trigger"`      // Trigger is a type that defines when to trigger the alert
	TriggerValues TriggerValues `json:"values"`       // Defines the values that cause the alert to trigger
	Name          string        `json:"name"`         // Name is the user-defined name for the alert
}

// AlertRulesStore stores rules for building tickscript alerting tasks
type AlertRulesStore interface {
	// All returns all rules in the store for the given source and kapacitor id
	All(ctx context.Context, sourceID, kapaID int) ([]AlertRule, error)
	// Add creates a new rule in the AlertRulesStore and returns AlertRule with ID for a given source and kapacitor id
	Add(ctx context.Context, sourceID, kapaID int, rule AlertRule) (AlertRule, error)
	// Delete the AlertRule from the store for a given source and kapacitor ID
	Delete(ctx context.Context, sourceID, kapaID int, rule AlertRule) error
	// Get retrieves AlertRule if `ID` exists within a given source and kapacitor id
	Get(ctx context.Context, sourceID, kapaID int, ID string) (AlertRule, error)
	// Update the AlertRule in the store within a given source and kapacitor id
	Update(ctx context.Context, sourceID, kapaID int, rule AlertRule) error
}

// TICKScript task to be used by kapacitor
type TICKScript string

// Ticker generates tickscript tasks for kapacitor
type Ticker interface {
	// Generate will create the tickscript to be used as a kapacitor task
	Generate(AlertRule) (TICKScript, error)
}

// TriggerValues specifies the alerting logic for a specific trigger type
type TriggerValues struct {
	Change   string `json:"change,omitempty"`   // Change specifies if the change is a percent or absolute
	Period   string `json:"period,omitempty"`   // Period length of time before deadman is alerted
	Shift    string `json:"shift,omitempty"`    // Shift is the amount of time to look into the past for the alert to compare to the present
	Operator string `json:"operator,omitempty"` // Operator for alert comparison
	Value    string `json:"value,omitempty"`    // Value is the boundary value when alert goes critical
}

// Field represent influxql fields and functions from the UI
type Field struct {
	Field string   `json:"field"`
	Funcs []string `json:"funcs"`
}

// GroupBy represents influxql group by tags from the UI
type GroupBy struct {
	Time string   `json:"time"`
	Tags []string `json:"tags"`
}

// QueryConfig represents UI query from the data explorer
type QueryConfig struct {
	ID              string              `json:"id,omitempty"`
	Database        string              `json:"database"`
	Measurement     string              `json:"measurement"`
	RetentionPolicy string              `json:"retentionPolicy"`
	Fields          []Field             `json:"fields"`
	Tags            map[string][]string `json:"tags"`
	GroupBy         GroupBy             `json:"groupBy"`
	AreTagsAccepted bool                `json:"areTagsAccepted"`
	RawText         string              `json:"rawText,omitempty"`
}

// Server represents a proxy connection to an HTTP server
type Server struct {
	ID       int    // ID is the unique ID of the server
	SrcID    int    // SrcID of the data source
	Name     string // Name is the user-defined name for the server
	Username string // Username is the username to connect to the server
	Password string // Password is in CLEARTEXT
	URL      string // URL are the connections to the server
}

// ServersStore stores connection information for a `Server`
type ServersStore interface {
	// All returns all servers in the store
	All(context.Context) ([]Server, error)
	// Add creates a new source in the ServersStore and returns Server with ID
	Add(context.Context, Server) (Server, error)
	// Delete the Server from the store
	Delete(context.Context, Server) error
	// Get retrieves Server if `ID` exists
	Get(ctx context.Context, ID int) (Server, error)
	// Update the Server in the store.
	Update(context.Context, Server) error
}

// ID creates uniq ID string
type ID interface {
	// Generate creates a unique ID string
	Generate() (string, error)
}

// UserID is a unique ID for a source user.
type UserID int

// User represents an authenticated user.
type User struct {
	ID    UserID `json:"id"`
	Email string `json:"email"`
}

// UsersStore is the Storage and retrieval of authentication information
type UsersStore interface {
	// Create a new User in the UsersStore
	Add(context.Context, *User) (*User, error)
	// Delete the User from the UsersStore
	Delete(context.Context, *User) error
	// Get retrieves a user if `ID` exists.
	Get(ctx context.Context, ID UserID) (*User, error)
	// Update the user's permissions or roles
	Update(context.Context, *User) error
	// FindByEmail will retrieve a user by email address.
	FindByEmail(ctx context.Context, Email string) (*User, error)
}

// ExplorationID is a unique ID for an Exploration.
type ExplorationID int

// Exploration is a serialization of front-end Data Explorer.
type Exploration struct {
	ID        ExplorationID
	Name      string    // User provided name of the Exploration.
	UserID    UserID    // UserID is the owner of this Exploration.
	Data      string    // Opaque blob of JSON data.
	CreatedAt time.Time // Time the exploration was first created.
	UpdatedAt time.Time // Latest time the exploration was updated.
	Default   bool      // Flags an exploration as the default.
}

// ExplorationStore stores front-end serializations of data explorer sessions.
type ExplorationStore interface {
	// Search the ExplorationStore for each Exploration owned by `UserID`.
	Query(ctx context.Context, userID UserID) ([]*Exploration, error)
	// Create a new Exploration in the ExplorationStore.
	Add(context.Context, *Exploration) (*Exploration, error)
	// Delete the Exploration from the ExplorationStore.
	Delete(context.Context, *Exploration) error
	// Retrieve an Exploration if `ID` exists.
	Get(ctx context.Context, ID ExplorationID) (*Exploration, error)
	// Update the Exploration; will also update the `UpdatedAt` time.
	Update(context.Context, *Exploration) error
}

// Cell is a rectangle and multiple time series queries to visualize.
type Cell struct {
	X       int32   `json:"x"`
	Y       int32   `json:"y"`
	W       int32   `json:"w"`
	H       int32   `json:"h"`
	I       string  `json:"i"`
	Name    string  `json:"name"`
	Queries []Query `json:"queries"`
}

// Layout is a collection of Cells for visualization
type Layout struct {
	ID          string `json:"id"`
	Application string `json:"app"`
	Measurement string `json:"measurement"`
	Autoflow    bool   `json:"autoflow"`
	Cells       []Cell `json:"cells"`
}

// LayoutStore stores dashboards and associated Cells
type LayoutStore interface {
	// All returns all dashboards in the store
	All(context.Context) ([]Layout, error)
	// Add creates a new dashboard in the LayoutStore
	Add(context.Context, Layout) (Layout, error)
	// Delete the dashboard from the store
	Delete(context.Context, Layout) error
	// Get retrieves Layout if `ID` exists
	Get(ctx context.Context, ID string) (Layout, error)
	// Update the dashboard in the store.
	Update(context.Context, Layout) error
}

// Principal is any entity that can be authenticated
type Principal string

// PrincipalKey is used to pass principal
// via context.Context to request-scoped
// functions.
const PrincipalKey Principal = "principal"

// Authenticator represents a service for authenticating users.
type Authenticator interface {
	// Authenticate returns User associated with token if successful.
	Authenticate(ctx context.Context, token string) (Principal, error)
	// Token generates a valid token for Principal lasting a duration
	Token(context.Context, Principal, time.Duration) (string, error)
}

// TokenExtractor extracts tokens from http requests
type TokenExtractor interface {
	// Extract will return the token or an error.
	Extract(r *http.Request) (string, error)
}
<commit_msg>Allow upper and lower bound to be zero<commit_after>package chronograf

import (
	"context"
	"net/http"
	"time"
)

// General errors.
const (
	ErrUpstreamTimeout     = Error("request to backend timed out")
	ErrExplorationNotFound = Error("exploration not found")
	ErrSourceNotFound      = Error("source not found")
	ErrServerNotFound      = Error("server not found")
	ErrLayoutNotFound      = Error("layout not found")
	ErrUserNotFound        = Error("user not found")
	ErrLayoutInvalid       = Error("layout is invalid")
	ErrAlertNotFound       = Error("alert not found")
	ErrAuthentication      = Error("user not authenticated")
)

// Error is a domain error encountered while processing chronograf requests
type Error string

func (e Error) Error() string {
	return string(e)
}

// Logger represents an abstracted structured logging implementation. It
// provides methods to trigger log messages at various alert levels and a
// WithField method to set keys for a structured log message.
type Logger interface {
	Debug(...interface{})
	Info(...interface{})
	Warn(...interface{})
	Error(...interface{})
	Fatal(...interface{})
	Panic(...interface{})

	WithField(string, interface{}) Logger
}

// Assets returns a handler to serve the website.
type Assets interface {
	Handler() http.Handler
}

// TimeSeries represents a queryable time series database.
type TimeSeries interface {
	// Query retrieves time series data from the database.
	Query(context.Context, Query) (Response, error)
	// Connect will connect to the time series using the information in `Source`.
	Connect(context.Context, *Source) error
}

// Range represents an upper and lower bound for data
type Range struct {
	Upper int64 `json:"upper"` // Upper is the upper bound
	Lower int64 `json:"lower"` // Lower is the lower bound
}

// Query retrieves a Response from a TimeSeries.
type Query struct {
	Command  string   `json:"query"`              // Command is the query itself
	DB       string   `json:"db,omitempty"`       // DB is optional and if empty will not be used.
	RP       string   `json:"rp,omitempty"`       // RP is a retention policy and optional; if empty will not be used.
	Wheres   []string `json:"wheres,omitempty"`   // Wheres restricts the query to certain attributes
	GroupBys []string `json:"groupbys,omitempty"` // GroupBys collate the query by these tags
	Label    string   `json:"label,omitempty"`    // Label is the Y-Axis label for the data
	Range    *Range   `json:"range,omitempty"`    // Range is the default Y-Axis range for the data
}

// Response is the result of a query against a TimeSeries
type Response interface {
	MarshalJSON() ([]byte, error)
}

// Source is connection information to a time-series data store.
type Source struct {
	ID       int    `json:"id,omitempty,string"` // ID is the unique ID of the source
	Name     string `json:"name"`                // Name is the user-defined name for the source
	Type     string `json:"type,omitempty"`      // Type specifies which kinds of source (enterprise vs oss)
	Username string `json:"username,omitempty"`  // Username is the username to connect to the source
	Password string `json:"password,omitempty"`  // Password is in CLEARTEXT
	URL      string `json:"url"`                 // URL are the connections to the source
	Default  bool   `json:"default"`             // Default specifies the default source for the application
	Telegraf string `json:"telegraf"`            // Telegraf is the db telegraf is written to.  By default it is "telegraf"
}

// SourcesStore stores connection information for a `TimeSeries`
type SourcesStore interface {
	// All returns all sources in the store
	All(context.Context) ([]Source, error)
	// Add creates a new source in the SourcesStore and returns Source with ID
	Add(context.Context, Source) (Source, error)
	// Delete the Source from the store
	Delete(context.Context, Source) error
	// Get retrieves Source if `ID` exists
	Get(ctx context.Context, ID int) (Source, error)
	// Update the Source in the store.
	Update(context.Context, Source) error
}

// AlertRule represents rules for building a tickscript alerting task
type AlertRule struct {
	ID            string        `json:"id,omitempty"` // ID is the unique ID of the alert
	Query         QueryConfig   `json:"query"`        // Query is the filter of data for the alert.
	Every         string        `json:"every"`        // Every how often to check for the alerting criteria
	Alerts        []string      `json:"alerts"`       // AlertServices name all the services to notify (e.g. pagerduty)
	Message       string        `json:"message"`      // Message included with alert
	Trigger       string        `json:"trigger"`      // Trigger is a type that defines when to trigger the alert
	TriggerValues TriggerValues `json:"values"`       // Defines the values that cause the alert to trigger
	Name          string        `json:"name"`         // Name is the user-defined name for the alert
}

// AlertRulesStore stores rules for building tickscript alerting tasks
type AlertRulesStore interface {
	// All returns all rules in the store for the given source and kapacitor id
	All(ctx context.Context, sourceID, kapaID int) ([]AlertRule, error)
	// Add creates a new rule in the AlertRulesStore and returns AlertRule with ID for a given source and kapacitor id
	Add(ctx context.Context, sourceID, kapaID int, rule AlertRule) (AlertRule, error)
	// Delete the AlertRule from the store for a given source and kapacitor ID
	Delete(ctx context.Context, sourceID, kapaID int, rule AlertRule) error
	// Get retrieves AlertRule if `ID` exists within a given source and kapacitor id
	Get(ctx context.Context, sourceID, kapaID int, ID string) (AlertRule, error)
	// Update the AlertRule in the store within a given source and kapacitor id
	Update(ctx context.Context, sourceID, kapaID int, rule AlertRule) error
}

// TICKScript task to be used by kapacitor
type TICKScript string

// Ticker generates tickscript tasks for kapacitor
type Ticker interface {
	// Generate will create the tickscript to be used as a kapacitor task
	Generate(AlertRule) (TICKScript, error)
}

// TriggerValues specifies the alerting logic for a specific trigger type
type TriggerValues struct {
	Change   string `json:"change,omitempty"`   // Change specifies if the change is a percent or absolute
	Period   string `json:"period,omitempty"`   // Period length of time before deadman is alerted
	Shift    string `json:"shift,omitempty"`    // Shift is the amount of time to look into the past for the alert to compare to the present
	Operator string `json:"operator,omitempty"` // Operator for alert comparison
	Value    string `json:"value,omitempty"`    // Value is the boundary value when alert goes critical
}

// Field represent influxql fields and functions from the UI
type Field struct {
	Field string   `json:"field"`
	Funcs []string `json:"funcs"`
}

// GroupBy represents influxql group by tags from the UI
type GroupBy struct {
	Time string   `json:"time"`
	Tags []string `json:"tags"`
}

// QueryConfig represents UI query from the data explorer
type QueryConfig struct {
	ID              string              `json:"id,omitempty"`
	Database        string              `json:"database"`
	Measurement     string              `json:"measurement"`
	RetentionPolicy string              `json:"retentionPolicy"`
	Fields          []Field             `json:"fields"`
	Tags            map[string][]string `json:"tags"`
	GroupBy         GroupBy             `json:"groupBy"`
	AreTagsAccepted bool                `json:"areTagsAccepted"`
	RawText         string              `json:"rawText,omitempty"`
}

// Server represents a proxy connection to an HTTP server
type Server struct {
	ID       int    // ID is the unique ID of the server
	SrcID    int    // SrcID of the data source
	Name     string // Name is the user-defined name for the server
	Username string // Username is the username to connect to the server
	Password string // Password is in CLEARTEXT
	URL      string // URL are the connections to the server
}

// ServersStore stores connection information for a `Server`
type ServersStore interface {
	// All returns all servers in the store
	All(context.Context) ([]Server, error)
	// Add creates a new source in the ServersStore and returns Server with ID
	Add(context.Context, Server) (Server, error)
	// Delete the Server from the store
	Delete(context.Context, Server) error
	// Get retrieves Server if `ID` exists
	Get(ctx context.Context, ID int) (Server, error)
	// Update the Server in the store.
	Update(context.Context, Server) error
}

// ID creates uniq ID string
type ID interface {
	// Generate creates a unique ID string
	Generate() (string, error)
}

// UserID is a unique ID for a source user.
type UserID int

// User represents an authenticated user.
type User struct {
	ID    UserID `json:"id"`
	Email string `json:"email"`
}

// UsersStore is the Storage and retrieval of authentication information
type UsersStore interface {
	// Create a new User in the UsersStore
	Add(context.Context, *User) (*User, error)
	// Delete the User from the UsersStore
	Delete(context.Context, *User) error
	// Get retrieves a user if `ID` exists.
	Get(ctx context.Context, ID UserID) (*User, error)
	// Update the user's permissions or roles
	Update(context.Context, *User) error
	// FindByEmail will retrieve a user by email address.
	FindByEmail(ctx context.Context, Email string) (*User, error)
}

// ExplorationID is a unique ID for an Exploration.
type ExplorationID int

// Exploration is a serialization of front-end Data Explorer.
type Exploration struct {
	ID        ExplorationID
	Name      string    // User provided name of the Exploration.
	UserID    UserID    // UserID is the owner of this Exploration.
	Data      string    // Opaque blob of JSON data.
	CreatedAt time.Time // Time the exploration was first created.
	UpdatedAt time.Time // Latest time the exploration was updated.
	Default   bool      // Flags an exploration as the default.
}

// ExplorationStore stores front-end serializations of data explorer sessions.
type ExplorationStore interface {
	// Search the ExplorationStore for each Exploration owned by `UserID`.
	Query(ctx context.Context, userID UserID) ([]*Exploration, error)
	// Create a new Exploration in the ExplorationStore.
	Add(context.Context, *Exploration) (*Exploration, error)
	// Delete the Exploration from the ExplorationStore.
	Delete(context.Context, *Exploration) error
	// Retrieve an Exploration if `ID` exists.
	Get(ctx context.Context, ID ExplorationID) (*Exploration, error)
	// Update the Exploration; will also update the `UpdatedAt` time.
	Update(context.Context, *Exploration) error
}

// Cell is a rectangle and multiple time series queries to visualize.
type Cell struct {
	X       int32   `json:"x"`
	Y       int32   `json:"y"`
	W       int32   `json:"w"`
	H       int32   `json:"h"`
	I       string  `json:"i"`
	Name    string  `json:"name"`
	Queries []Query `json:"queries"`
}

// Layout is a collection of Cells for visualization
type Layout struct {
	ID          string `json:"id"`
	Application string `json:"app"`
	Measurement string `json:"measurement"`
	Autoflow    bool   `json:"autoflow"`
	Cells       []Cell `json:"cells"`
}

// LayoutStore stores dashboards and associated Cells
type LayoutStore interface {
	// All returns all dashboards in the store
	All(context.Context) ([]Layout, error)
	// Add creates a new dashboard in the LayoutStore
	Add(context.Context, Layout) (Layout, error)
	// Delete the dashboard from the store
	Delete(context.Context, Layout) error
	// Get retrieves Layout if `ID` exists
	Get(ctx context.Context, ID string) (Layout, error)
	// Update the dashboard in the store.
	Update(context.Context, Layout) error
}

// Principal is any entity that can be authenticated
type Principal string

// PrincipalKey is used to pass principal
// via context.Context to request-scoped
// functions.
const PrincipalKey Principal = "principal"

// Authenticator represents a service for authenticating users.
type Authenticator interface {
	// Authenticate returns User associated with token if successful.
	Authenticate(ctx context.Context, token string) (Principal, error)
	// Token generates a valid token for Principal lasting a duration
	Token(context.Context, Principal, time.Duration) (string, error)
}

// TokenExtractor extracts tokens from http requests
type TokenExtractor interface {
	// Extract will return the token or an error.
	Extract(r *http.Request) (string, error)
}
<|endoftext|>


<commit_before>package air

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net"
	"os"
	"time"
)

// Config is a global set of configurations that for an instance of `Air`
// for customization.
type Config struct {
	// AppName represens the name of current `Air` instance.
	AppName string

	// DebugMode represents the state of `Air`'s debug mode.
	//
	// Default value is false.
	//
	// It's called "debug_mode" in the config file.
	DebugMode bool

	// LogFormat represents the format of `Logger`'s output content.
	//
	// Default value is:
	// `{"app_name":"{{.app_name}}","time":"{{.time_rfc3339}}",` +
	// `"level":"{{.level}}","file":"{{.short_file}}","line":"{{.line}}"}`
	//
	// It's called "log_format" in the config file.
	LogFormat string

	// Address represents the TCP address that `Server` to listen
	// on.
	//
	// Default value is "localhost:8080".
	//
	// It's called "address" in the config file.
	Address string

	// Listener represens the custom `net.Listener`. If set, `Server`
	// accepts connections on it.
	//
	// Default value is nil.
	Listener net.Listener

	// TLSCertFile represents the TLS certificate file path.
	//
	// Default value is "".
	//
	// It's called "tls_cert_file" in the config file.
	TLSCertFile string

	// TLSKeyFile represents the TLS key file path.
	//
	// Default value is "".
	//
	// It's called "tls_key_file" in the config file.
	TLSKeyFile string

	// ReadTimeout represents the maximum duration before timing out
	// read of the request.
	//
	// Default value is 0.
	//
	// It's called "read_timeout" in the config file.
	//
	// *It's unit in the config file is SECONDS.*
	ReadTimeout time.Duration

	// WriteTimeout represents the maximum duration before timing
	// out write of the response.
	//
	// Default value is 0.
	//
	// It's called "write_timeout" in the config file.
	//
	// *It's unit in the config file is SECONDS.*
	WriteTimeout time.Duration

	// TemplatesRoot represents the root directory of the html templates.
	// It will be parsed into `Renderer`.
	//
	// Default value is "templates" that means a subdirectory of the
	// runtime directory.
	//
	// It's called "templates_root" in the config file.
	TemplatesRoot string

	// Data represents the data that parsing from config file. You can
	// use it to access the values in the config file.
	//
	// e.g. c.Data["mysql_user_name"] will accesses the value in config
	// file called "mysql_user_name".
	Data JSONMap
}

// defaultConfig is the default instance of `Config`.
var defaultConfig Config

// configs is the JSON map that stored all the configs parsing from
// config file.
var configs JSONMap

func init() {
	defaultConfig = Config{
		LogFormat: `{"app_name":"{{.app_name}}","time":"{{.time_rfc3339}}",` +
			`"level":"{{.level}}","file":"{{.short_file}}","line":"{{.line}}"}`,
		Address:       "localhost:8080",
		TemplatesRoot: "templates",
	}

	var cfn = "config.json"
	_, err := os.Stat(cfn)
	if err == nil || os.IsExist(err) {
		bytes, err := ioutil.ReadFile(cfn)
		if err != nil {
			panic(err)
		}
		err = json.Unmarshal(bytes, &configs)
		if err != nil {
			panic(err)
		}
		if len(configs) == 0 {
			panic("need at least one app in the config file or remove the config file")
		}
	}
}

// NewConfig returns a new instance of `Config` with a appName by parsing
// the config file that in the rumtime directory named "config.json".
// NewConfig returns the defaultConfig(field "AppName" be setted to provided
// appName) if the config file or appName doesn't exist.
func NewConfig(appName string) *Config {
	c := defaultConfig
	switch {
	case configs == nil:
		c.AppName = appName
	case len(configs) == 1:
		for k, v := range configs {
			c.AppName = k
			c.Data = v.(map[string]interface{})
			c.fill()
		}
	case configs[appName] == nil:
		panic(fmt.Sprintf("app %s does not exist in the config file", appName))
	default:
		c.AppName = appName
		c.Data = configs[appName].(map[string]interface{})
		c.fill()
	}
	return &c
}

// fill fills field's value from field `Data` of c. It fills field's
// value from defaultConfig if target value in `Data` of c does not
// exist.
func (c *Config) fill() {
	if dm, ok := c.Data["debug_mode"]; ok {
		c.DebugMode = dm.(bool)
	}
	if lf, ok := c.Data["log_format"]; ok {
		c.LogFormat = lf.(string)
	}
	if addr, ok := c.Data["address"]; ok {
		c.Address = addr.(string)
	}
	if tlscf, ok := c.Data["tls_cert_file"]; ok {
		c.TLSCertFile = tlscf.(string)
	}
	if tlskf, ok := c.Data["tls_key_file"]; ok {
		c.TLSKeyFile = tlskf.(string)
	}
	if rt, ok := c.Data["read_timeout"]; ok {
		c.ReadTimeout = time.Duration(rt.(float64)) * time.Second
	}
	if wt, ok := c.Data["write_timeout"]; ok {
		c.WriteTimeout = time.Duration(wt.(float64)) * time.Second
	}
	if tr, ok := c.Data["templates_root"]; ok {
		c.TemplatesRoot = tr.(string)
	}
}
<commit_msg>refactor: justify comments in config.go<commit_after>package air

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net"
	"os"
	"time"
)

// Config is a global set of configurations that for an instance of `Air`
// for customization.
type Config struct {
	// AppName represens the name of current `Air` instance.
	AppName string

	// DebugMode represents the state of `Air`'s debug mode.
	//
	// Default value is false.
	//
	// It's called "debug_mode" in the config file.
	DebugMode bool

	// LogFormat represents the format of `Logger`'s output content.
	//
	// Default value is:
	// `{"app_name":"{{.app_name}}","time":"{{.time_rfc3339}}",` +
	// `"level":"{{.level}}","file":"{{.short_file}}","line":"{{.line}}"}`
	//
	// It's called "log_format" in the config file.
	LogFormat string

	// Address represents the TCP address that `Server` to listen
	// on.
	//
	// Default value is "localhost:8080".
	//
	// It's called "address" in the config file.
	Address string

	// Listener represens the custom `net.Listener`. If set, `Server`
	// accepts connections on it.
	//
	// Default value is nil.
	Listener net.Listener

	// TLSCertFile represents the TLS certificate file path.
	//
	// Default value is "".
	//
	// It's called "tls_cert_file" in the config file.
	TLSCertFile string

	// TLSKeyFile represents the TLS key file path.
	//
	// Default value is "".
	//
	// It's called "tls_key_file" in the config file.
	TLSKeyFile string

	// ReadTimeout represents the maximum duration before timing out
	// read of the request.
	//
	// Default value is 0.
	//
	// It's called "read_timeout" in the config file.
	//
	// *It's unit in the config file is SECONDS.*
	ReadTimeout time.Duration

	// WriteTimeout represents the maximum duration before timing
	// out write of the response.
	//
	// Default value is 0.
	//
	// It's called "write_timeout" in the config file.
	//
	// *It's unit in the config file is SECONDS.*
	WriteTimeout time.Duration

	// TemplatesRoot represents the root directory of the html templates.
	// It will be parsed into `Renderer`.
	//
	// Default value is "templates" that means a subdirectory of the
	// runtime directory.
	//
	// It's called "templates_root" in the config file.
	TemplatesRoot string

	// Data represents the data that parsing from config file. You can
	// use it to access the values in the config file.
	//
	// e.g. c.Data["mysql_user_name"] will accesses the value in config
	// file called "mysql_user_name".
	Data JSONMap
}

// defaultConfig is the default instance of `Config`.
var defaultConfig Config

// configs is the JSON map that stored all the configs parsing from
// config file.
var configs JSONMap

func init() {
	defaultConfig = Config{
		LogFormat: `{"app_name":"{{.app_name}}","time":"{{.time_rfc3339}}",` +
			`"level":"{{.level}}","file":"{{.short_file}}","line":"{{.line}}"}`,
		Address:       "localhost:8080",
		TemplatesRoot: "templates",
	}

	var cfn = "config.json"
	_, err := os.Stat(cfn)
	if err == nil || os.IsExist(err) {
		bytes, err := ioutil.ReadFile(cfn)
		if err != nil {
			panic(err)
		}
		err = json.Unmarshal(bytes, &configs)
		if err != nil {
			panic(err)
		}
		if len(configs) == 0 {
			panic("need at least one app in the config file or remove the config file")
		}
	}
}

// NewConfig returns a new instance of `Config` with a appName by parsing
// the config file that in the rumtime directory named "config.json".
//
// NewConfig returns the defaultConfig(field "AppName" be setted to provided
// appName) if the config file doesn't exist.
func NewConfig(appName string) *Config {
	c := defaultConfig
	switch {
	case configs == nil:
		c.AppName = appName
	case len(configs) == 1:
		for k, v := range configs {
			c.AppName = k
			c.Data = v.(map[string]interface{})
			c.fillData()
		}
	case configs[appName] == nil:
		panic(fmt.Sprintf("app %s does not exist in the config file", appName))
	default:
		c.AppName = appName
		c.Data = configs[appName].(map[string]interface{})
		c.fillData()
	}
	return &c
}

// fillData fills field's value from field `Data` of c.
func (c *Config) fillData() {
	if dm, ok := c.Data["debug_mode"]; ok {
		c.DebugMode = dm.(bool)
	}
	if lf, ok := c.Data["log_format"]; ok {
		c.LogFormat = lf.(string)
	}
	if addr, ok := c.Data["address"]; ok {
		c.Address = addr.(string)
	}
	if tlscf, ok := c.Data["tls_cert_file"]; ok {
		c.TLSCertFile = tlscf.(string)
	}
	if tlskf, ok := c.Data["tls_key_file"]; ok {
		c.TLSKeyFile = tlskf.(string)
	}
	if rt, ok := c.Data["read_timeout"]; ok {
		c.ReadTimeout = time.Duration(rt.(float64)) * time.Second
	}
	if wt, ok := c.Data["write_timeout"]; ok {
		c.WriteTimeout = time.Duration(wt.(float64)) * time.Second
	}
	if tr, ok := c.Data["templates_root"]; ok {
		c.TemplatesRoot = tr.(string)
	}
}
<|endoftext|>