<commit_before>package cli

import (
	"fmt"
	"log"
	//	"gopkg.in/yaml.v2"
	//	"io/ioutil"
	//	"text/template"
	"github.com/qadium/plumber/graph"
	"github.com/qadium/plumber/shell"
	"os/exec"
	"path/filepath"
	// "golang.org/x/oauth2/google"
	// "golang.org/x/oauth2"
	// "google.golang.org/cloud"
	// "google.golang.org/cloud/container"
	// kubectl "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd"
	// cmdutil "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util"
	// "os"
)

func contextsToGraph(ctxs []*Context) []*graph.Node {
	nodes := make([]*graph.Node, len(ctxs))
	m := make(map[string]int) // this map maps inputs to the index of the
	// node that uses it
	// build a map to create the DAG
	for i, ctx := range ctxs {
		nodes[i] = graph.NewNode(ctx.Name)
		for _, input := range ctx.Inputs {
			m[input.Name] = i
		}
	}

	for i, ctx := range ctxs {
		for _, output := range ctx.Outputs {
			if v, ok := m[output.Name]; ok {
				nodes[i].Children = append(nodes[i].Children, nodes[v])
			}
		}
	}
	return nodes
}

func Start(pipeline string, gce string) error {
	log.Printf("==> Starting '%s' pipeline", pipeline)
	defer log.Printf("<== '%s' finished.", pipeline)

	path, err := pipelinePath(pipeline)
	if err != nil {
		return err
	}

	if gce != "" {
		// we can probably get the project name with google cloud SDK

		// step 1. re-tag local containers to gcr.io/$GCE/$pipeline-$bundlename
		// step 2. push them to gce
		// step 3. generate k8s files in pipelinePath
		// step 4. launch all the services
		err := shell.RunAndLog("kubectl", "create", "-f", fmt.Sprintf("%s/k8s", path))
		if err != nil {
			return err
		}
		// step 5: open up the firewall?
		return nil
	}
	log.Printf("NO GCE ID provided. running locally")
	// start GOOGLE experiments?
	// when start is invoked with --gce PROJECT_ID, this piece of code
	// should be run
	// client, err := google.DefaultClient(oauth2.NoContext, "https://www.googleapis.com/auth/compute")
	// if err != nil {
	// 	return err
	// }
	// cloudCtx := cloud.NewContext("kubernetes-fun", client)
	//
	// resources, err := container.Clusters(cloudCtx, "")
	// if err != nil {
	// 	return err
	// }
	// for _, op := range resources {
	// 	log.Printf("%v", op)
	// }
	//
	// loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
	// log.Printf("loading rules: %v", *loadingRules)
	// configOverrides := &clientcmd.ConfigOverrides{}
	// kubeConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, configOverrides)
	// cfg, err := kubeConfig.ClientConfig()
	// if err != nil {
	// 	return err
	// }

	// well, we just shell out!
	// f := cmdutil.NewFactory(nil)
	// cmd := kubectl.NewCmdCreate(f, os.Stdout)
	// f.BindFlags(cmd.PersistentFlags())
	// cmd.Flags().Set("filename", "/Users/echu/.plumber/foo/k8s")
	// cmd.Run(cmd, []string{})

	// end GOOGLE experiments

	log.Printf(" |  Building dependency graph.")
	configs, err := filepath.Glob(fmt.Sprintf("%s/*.yml", path))
	if err != nil {
		return err
	}

	ctxs := make([]*Context, len(configs))
	for i, config := range configs {
		ctxs[i], err = parseConfig(config)
		if err != nil {
			return err
		}
	}

	// graph with diamond (test case)
	// n1 := graph.NewNode("foo")
	// n2 := graph.NewNode("bar")
	// n3 := graph.NewNode("joe")
	// n4 := graph.NewNode("bob")
	// n1.Children = append(n1.Children, n2, n3)
	// n2.Children = append(n2.Children, n4)
	// n3.Children = append(n3.Children, n4)

	g := contextsToGraph(ctxs)
	sorted, err := graph.ReverseTopoSort(g)
	if err != nil {
		return err
	}
	log.Printf("    Reverse sorted: %v", sorted)
	log.Printf("    Completed.")

	log.Printf(" |  Starting bundles...")
	managerDockerArgs := []string{"run", "-p", "9800:9800", "--rm", "plumber/manager"}
	// walk through the reverse sorted bundles and start them up
	for i := len(sorted) - 1; i >= 0; i-- {
		bundleName := sorted[i]
		log.Printf("    Starting: '%s'", bundleName)
		cmd := exec.Command("docker", "run", "-d", "-P", fmt.Sprintf("plumber/%s", bundleName))
		containerId, err := cmd.Output()
		if err != nil {
			return err
		}

		defer func() {
			log.Printf("    Stopping: '%s'", bundleName)
			cmd := exec.Command("docker", "rm", "-f", string(containerId)[0:4])
			_, err := cmd.Output()
			if err != nil {
				panic(err)
			}
			log.Printf("    Stopped.")
		}()

		log.Printf("    Started: %s", string(containerId))
		cmd = exec.Command("docker", "inspect", "--format='{{(index (index .NetworkSettings.Ports \"9800/tcp\") 0).HostPort}}'", string(containerId)[0:4])
		portNum, err := cmd.Output()
		if err != nil {
			return err
		}
		// should use docker host IP (for local deploy)
		// should use "names" for kubernetes deploy
		managerDockerArgs = append(managerDockerArgs, fmt.Sprintf("http://172.17.42.1:%s", string(portNum[:len(portNum)-1])))
	}
	log.Printf("    Args passed to 'docker': %v", managerDockerArgs)
	log.Printf("    Done.")

	log.Printf(" |  Running manager. CTRL-C to quit.")
	err = shell.RunAndLog("docker", managerDockerArgs...)
	if err != nil {
		return err
	}
	log.Printf("    Done.")
	return nil
}
<commit_msg>updated remote start<commit_after>package cli

import (
	"fmt"
	"log"
	//	"gopkg.in/yaml.v2"
	"os"
	//	"text/template"
	"github.com/qadium/plumber/bindata"
	"github.com/qadium/plumber/graph"
	"github.com/qadium/plumber/shell"
	"os/exec"
	"path/filepath"
	"text/template"
	// "golang.org/x/oauth2/google"
	// "golang.org/x/oauth2"
	// "google.golang.org/cloud"
	// "google.golang.org/cloud/container"
	// kubectl "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd"
	// cmdutil "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util"
	// "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd"
	// "os"
)

type pipelineInfo struct {
	path string
	name string
	commit string
	plumberVersion string
	plumberCommit string
}

type kubeData struct {
	BundleName string
	ExternalFacing bool
	PipelineName string
	PipelineCommit string
	PlumberVersion string
	PlumberCommit string
	ImageName string
	Args []string
}

func contextsToGraph(ctxs []*Context) []*graph.Node {
	nodes := make([]*graph.Node, len(ctxs))
	m := make(map[string]int) // this map maps inputs to the index of the
	// node that uses it
	// build a map to create the DAG
	for i, ctx := range ctxs {
		nodes[i] = graph.NewNode(ctx.Name)
		for _, input := range ctx.Inputs {
			m[input.Name] = i
		}
	}

	for i, ctx := range ctxs {
		for _, output := range ctx.Outputs {
			if v, ok := m[output.Name]; ok {
				nodes[i].Children = append(nodes[i].Children, nodes[v])
			}
		}
	}
	return nodes
}

func localStart(sortedPipeline []string) error {
	log.Printf(" |  Starting bundles...")
	managerDockerArgs := []string{"run", "-p", "9800:9800", "--rm", "plumber/manager"}
	// walk through the reverse sorted bundles and start them up
	for i := len(sortedPipeline) - 1; i >= 0; i-- {
		bundleName := sortedPipeline[i]
		log.Printf("    Starting: '%s'", bundleName)
		cmd := exec.Command("docker", "run", "-d", "-P", fmt.Sprintf("plumber/%s", bundleName))
		containerId, err := cmd.Output()
		if err != nil {
			return err
		}

		defer func() {
			log.Printf("    Stopping: '%s'", bundleName)
			cmd := exec.Command("docker", "rm", "-f", string(containerId)[0:4])
			_, err := cmd.Output()
			if err != nil {
				panic(err)
			}
			log.Printf("    Stopped.")
		}()

		log.Printf("    Started: %s", string(containerId))
		cmd = exec.Command("docker", "inspect", "--format='{{(index (index .NetworkSettings.Ports \"9800/tcp\") 0).HostPort}}'", string(containerId)[0:4])
		portNum, err := cmd.Output()
		if err != nil {
			return err
		}
		// should use docker host IP (for local deploy)
		// should use "names" for kubernetes deploy
		managerDockerArgs = append(managerDockerArgs, fmt.Sprintf("http://172.17.42.1:%s", string(portNum[:len(portNum)-1])))
	}
	log.Printf("    Done.")
	log.Printf("    Args passed to 'docker': %v", managerDockerArgs)

	log.Printf(" |  Running manager. CTRL-C to quit.")
	err := shell.RunAndLog("docker", managerDockerArgs...)
	if err != nil {
		return err
	}
	log.Printf("    Done.")
	return nil
}

func writeKubernetesTemplate(tmplType string, destFilename string, templateData kubeData) error {
	tmpl, err := bindata.Asset(fmt.Sprintf("templates/%s.yaml", tmplType))
	if err != nil {
		return err
	}

	file, err := os.Create(destFilename)
	if err != nil {
		return err
	}
	defer func() {
		if err := file.Close(); err != nil {
			panic(err)
		}
	}()

	tmplFile, err := template.New("template").Parse(string(tmpl))
	if err != nil {
		return err
	}
	if err := tmplFile.Execute(file, templateData); err != nil {
		return err
	}
	return nil
}

func writeKubernetesFiles(templateData kubeData) error {
	log.Printf(" |     Writing '%s'", templateData.BundleName)
	k8s, err := KubernetesPath(templateData.PipelineName)
	if err != nil {
		return err
	}

	log.Printf("       Creating service file.")
	err = writeKubernetesTemplate("service", fmt.Sprintf("%s/%s.yaml", k8s, templateData.BundleName), templateData)
	if err != nil {
		return err
	}
	log.Printf("       Created.")

	log.Printf("       Creating replication controller file.")
	err = writeKubernetesTemplate("replication-controller", fmt.Sprintf("%s/%s-rc.yaml", k8s, templateData.BundleName), templateData)
	if err != nil {
		return err
	}
	log.Printf("       Created.")
	log.Printf("       Done.")
	return nil
}

func remoteStart(sortedPipeline []string, projectId string, pipeline pipelineInfo) error {
	// we can probably get the project name with google cloud SDK
	log.Printf("   Creating 'k8s' directory...")
	k8s, err := KubernetesPath(pipeline.name)
	if err != nil {
		return err
	}

	if err := os.MkdirAll(k8s, 0755); err != nil {
		return err
	}
	log.Printf("   Created.")

	args := []string{}

	for i := len(sortedPipeline) - 1; i >= 0; i-- {
		bundleName := sortedPipeline[i]
		localDockerTag := fmt.Sprintf("plumber/%s", bundleName)
		remoteDockerTag := fmt.Sprintf("gcr.io/%s/plumber-%s", projectId, bundleName)
		data := kubeData {
			BundleName: bundleName,
			ImageName: remoteDockerTag,
			PlumberVersion: pipeline.plumberVersion,
			PlumberCommit: pipeline.plumberCommit,
			PipelineName: pipeline.name,
			PipelineCommit: pipeline.commit,
			ExternalFacing: false,
			Args: []string{},
		}

		// step 1. re-tag local containers to gcr.io/$GCE/$pipeline-$bundlename
		log.Printf("    Retagging: '%s'", bundleName)
		err := shell.RunAndLog("docker", "tag", "-f", localDockerTag, remoteDockerTag)
		if err != nil {
			return err
		}
		// step 2. push them to gce
		log.Printf("    Submitting: '%s'", remoteDockerTag)
		err = shell.RunAndLog("gcloud", "preview", "docker", "push", remoteDockerTag)
		if err != nil {
			return err
		}
		// step 3. generate k8s files in pipelinePath
		if err := writeKubernetesFiles(data); err != nil {
			return err
		}

		// append to arglist (args now in sorted order)
		args = append(args, fmt.Sprintf("http://%s:9800", bundleName))
	}
	// create the manager service
	data := kubeData {
		BundleName: "manager",
		ImageName: fmt.Sprintf("gcr.io/%s/plumber-manager", projectId),
		PlumberVersion: pipeline.plumberVersion,
		PlumberCommit: pipeline.plumberCommit,
		PipelineName: pipeline.name,
		PipelineCommit: pipeline.commit,
		ExternalFacing: true,
		Args: args,
	}
	// step 1. re-tag local containers to gcr.io/$GCE/$pipeline-$bundlename
	log.Printf("    Retagging: '%s'", data.BundleName)
	err = shell.RunAndLog("docker", "tag", "-f", "plumber/manager", data.ImageName)
	if err != nil {
		return err
	}
	// step 2. push them to gce
	log.Printf("    Submitting: '%s'", data.ImageName)
	err = shell.RunAndLog("gcloud", "preview", "docker", "push", data.ImageName)
	if err != nil {
		return err
	}
	// step 3. generate k8s file in pipeline
	if err := writeKubernetesFiles(data); err != nil {
		return err
	}

	// step 4. launch all the services
	err = shell.RunAndLog("kubectl", "create", "-f", fmt.Sprintf("%s/k8s", pipeline.path))
	if err != nil {
		return err
	}
	// step 5: open up the firewall?
	return nil
}

func Start(pipeline, gce, plumberVersion, plumberGitCommit string) error {
	log.Printf("==> Starting '%s' pipeline", pipeline)
	defer log.Printf("<== '%s' finished.", pipeline)

	log.Printf(" |  Building dependency graph.")
	path, err := GetPipeline(pipeline)
	if err != nil {
		return err
	}

	configs, err := filepath.Glob(fmt.Sprintf("%s/*.yml", path))
	if err != nil {
		return err
	}

	ctxs := make([]*Context, len(configs))
	for i, config := range configs {
		ctxs[i], err = ParseConfig(config)
		if err != nil {
			return err
		}
	}

	// graph with diamond (test case)
	// n1 := graph.NewNode("foo")
	// n2 := graph.NewNode("bar")
	// n3 := graph.NewNode("joe")
	// n4 := graph.NewNode("bob")
	// n1.Children = append(n1.Children, n2, n3)
	// n2.Children = append(n2.Children, n4)
	// n3.Children = append(n3.Children, n4)

	g := contextsToGraph(ctxs)
	sortedPipeline, err := graph.ReverseTopoSort(g)
	if err != nil {
		return err
	}
	log.Printf("    Reverse sorted: %v", sortedPipeline)
	log.Printf("    Completed.")

	if gce != "" {
		// start GOOGLE experiments?
		// when start is invoked with --gce PROJECT_ID, this piece of code
		// should be run
		// client, err := google.DefaultClient(oauth2.NoContext, "https://www.googleapis.com/auth/compute")
		// if err != nil {
		// 	return err
		// }
		// cloudCtx := cloud.NewContext("kubernetes-fun", client)
		//
		// resources, err := container.Clusters(cloudCtx, "")
		// if err != nil {
		// 	return err
		// }
		// for _, op := range resources {
		// 	log.Printf("%v", op)
		// }
		//
		// loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
		// log.Printf("loading rules: %v", *loadingRules)
		// configOverrides := &clientcmd.ConfigOverrides{}
		// kubeConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, configOverrides)
		// cfg, err := kubeConfig.ClientConfig()
		// if err != nil {
		// 	return err
		// }

		// well, we just shell out!
		// f := cmdutil.NewFactory(nil)
		// cmd := kubectl.NewCmdCreate(f, os.Stdout)
		// f.BindFlags(cmd.PersistentFlags())
		// cmd.Flags().Set("filename", "/Users/echu/.plumber/foo/k8s")
		// cmd.Run(cmd, []string{})

		// end GOOGLE experiments

		info := pipelineInfo {
			name: pipeline,
			path: path,
			commit: "",
			plumberVersion: plumberVersion,
			plumberCommit: plumberGitCommit,
		}
		log.Printf(" |  Running remote pipeline.")
		return remoteStart(sortedPipeline, gce, info)
	} else {
		log.Printf(" |  Running local pipeline.")
		return localStart(sortedPipeline)
	}
	return nil
}
<|endoftext|>


<commit_before>package regexutil

import (
	"reflect"
	"testing"

	"../db/sqlconstants"
)

const TEST_SQLITE3_CREATE_STATEMENT_A = "CREATE TABLE FOO ( X INTEGER, Y TEXT PRIMARY KEY)"
const TEST_SQLITE3_CREATE_STATEMENT_B = "CREATE TABLE FOO ( X INTEGER, Y TEXT PRIMARY KEY, Z BETA NOT NULL)"
const TEST_SQLITE3_CREATE_STATEMENT_C = "CREATE TABLE FOO ( X INTEGER, PRIMARY KEY Y, Z BETA NOT NULL)"
const TEST_SQLITE3_CREATE_STATEMENT_D = "CREATE TABLE FOO ( X INTEGER, Y TEXT PRIMARY KEY, Z BETA NOT NULL, T DEFAULT  CURRENT_TIMESTAMP)"

func TestReturnNameGroupValueMap(t *testing.T) {
	_, err := ParseAndReturnNameGroupValueMap(SQLITE3_CREATE_TABLE_FIRST_PASS_PARSER, TEST_SQLITE3_CREATE_STATEMENT_A)

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

}

func TestFirstPassParseCreateStatement(t *testing.T) {
	_, _, err := firstPassParseCreateStatement(TEST_SQLITE3_CREATE_STATEMENT_A)
	if err != nil {
		t.Error(err)
	}

}

func TestSecondPassParseCreateStatement(t *testing.T) {
	_, columns, err := firstPassParseCreateStatement(sqlconstants.SQLITE3_CREATE_BUCKET_SCHEMA)
	if err != nil {
		t.Error(err)
	}

	_, err = secondPassParseCreateStatement(columns)

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

	_, columns, err = firstPassParseCreateStatement(TEST_SQLITE3_CREATE_STATEMENT_B)
	if err != nil {
		t.Error(err)
	}

	_, err = secondPassParseCreateStatement(columns)

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

	_, columns, err = firstPassParseCreateStatement(TEST_SQLITE3_CREATE_STATEMENT_C)
	if err != nil {
		t.Error(err)
	}

	_, err = secondPassParseCreateStatement(columns)

	if err == nil {
		t.Error("Error should have been caught")
	}

}

func TestParseCreateStatement(t *testing.T) {

	//CASE A

	table, columnArrayMap, err := ParseCreateStatement(TEST_SQLITE3_CREATE_STATEMENT_A)

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

	if table != "FOO" {
		t.Error("error table name is incorrect")
	}

	cam := []map[string]string{}
	c1 := map[string]string{"": "X INTEGER", "columntype": "INTEGER", "columnname": "X", "constraints": ""}
	c2 := map[string]string{"": "Y TEXT PRIMARY KEY", "columntype": "TEXT", "columnname": "Y", "constraints": "PRIMARY KEY"}

	cam = append(cam, c1)
	cam = append(cam, c2)

	if !reflect.DeepEqual(columnArrayMap, cam) {
		t.Error("ParseCreateStatement created unexpected results => test case ->", TEST_SQLITE3_CREATE_STATEMENT_A)
		t.Error("test data =>", cam)
		t.Error("returned data =>", columnArrayMap)
	}

	//CASE B

	table, columnArrayMap, err = ParseCreateStatement(TEST_SQLITE3_CREATE_STATEMENT_B)

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

	if table != "FOO" {
		t.Error("error table name is incorrect")
	}

	cam = []map[string]string{}
	c1 = map[string]string{"": "X INTEGER", "columntype": "INTEGER", "columnname": "X", "constraints": ""}
	c2 = map[string]string{"": "Y TEXT PRIMARY KEY", "columntype": "TEXT", "columnname": "Y", "constraints": "PRIMARY KEY"}
	c3 := map[string]string{"": "Z BETA NOT NULL", "columntype": "BETA", "columnname": "Z", "constraints": "NOT NULL"}

	cam = append(cam, c1)
	cam = append(cam, c2)
	cam = append(cam, c3)

	if !reflect.DeepEqual(columnArrayMap, cam) {
		t.Error("ParseCreateStatement created unexpected results => test case ->", TEST_SQLITE3_CREATE_STATEMENT_B)
		t.Error("test data =>", cam)
		t.Error("returned data =>", columnArrayMap)
	}

	//CASE C

	table, columnArrayMap, err = ParseCreateStatement(TEST_SQLITE3_CREATE_STATEMENT_C)

	if err == nil {
		t.Error("ERROR should be thrown")
	}

	//CASE D
	table, columnArrayMap, err = ParseCreateStatement(TEST_SQLITE3_CREATE_STATEMENT_D)

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

	if table != "FOO" {
		t.Error("error table name is incorrect")
	}

	cam = []map[string]string{}
	c1 = map[string]string{"": "X INTEGER", "columntype": "INTEGER", "columnname": "X", "constraints": ""}
	c2 = map[string]string{"": "Y TEXT PRIMARY KEY", "columntype": "TEXT", "columnname": "Y", "constraints": "PRIMARY KEY"}
	c3 = map[string]string{"": "Z BETA NOT NULL", "columntype": "BETA", "columnname": "Z", "constraints": "NOT NULL"}
	c4 := map[string]string{"": "T DEFAULT  CURRENT_TIMESTAMP", "columntype": "TIMESTAMP", "columnname": "T", "constraints": "DEFAULT  CURRENT_TIMESTAMP"}

	cam = append(cam, c1)
	cam = append(cam, c2)
	cam = append(cam, c3)
	cam = append(cam, c4)

	if !reflect.DeepEqual(columnArrayMap, cam) {
		t.Error("ParseCreateStatement created unexpected results => test case ->", TEST_SQLITE3_CREATE_STATEMENT_D)
		t.Error("test data =>", cam)
		t.Error("returned data =>", columnArrayMap)
	}

}
<commit_msg>added method to test for table name in select<commit_after>package regexutil

import (
	"reflect"
	"testing"

	"../db/sqlconstants"
)

const TEST_SQLITE3_CREATE_STATEMENT_A = "CREATE TABLE FOO ( X INTEGER, Y TEXT PRIMARY KEY)"
const TEST_SQLITE3_CREATE_STATEMENT_B = "CREATE TABLE FOO ( X INTEGER, Y TEXT PRIMARY KEY, Z BETA NOT NULL)"
const TEST_SQLITE3_CREATE_STATEMENT_C = "CREATE TABLE FOO ( X INTEGER, PRIMARY KEY Y, Z BETA NOT NULL)"
const TEST_SQLITE3_CREATE_STATEMENT_D = "CREATE TABLE FOO ( X INTEGER, Y TEXT PRIMARY KEY, Z BETA NOT NULL, T DEFAULT  CURRENT_TIMESTAMP)"

const TEST_SQLITE_SELECT_STATEMENT_A = "SELECT * FROM FOO WHERE X = 2 AND Y = 3"
const TEST_SQLITE_SELECT_STATEMENT_B = "SELECT NAME, ADDRESS FROM FOOBAR WHERE ID IS NOT NULL;"

const TEST_SQLITE_SELECT_STATEMENT_C = "SELECT NAME,ADDRESS FROM FOO;"

func TestTableNameFromSelect(t *testing.T) {
	tableName, err := TableNameFromSelect(TEST_SQLITE_SELECT_STATEMENT_A)

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

	if tableName != "FOO" {
		t.Error("table name does not equal ", "FOO")

	}

	tableName, err = TableNameFromSelect(TEST_SQLITE_SELECT_STATEMENT_B)

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

	if tableName != "FOOBAR" {
		t.Error("table name does not equal ", "FOO")
	}

	tableName, err = TableNameFromSelect(TEST_SQLITE_SELECT_STATEMENT_C)

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

	if tableName != "FOO" {
		t.Error("table name does not equal ", "FOO")
		t.Error("table name actually is ", tableName)
	}

}

func TestReturnNameGroupValueMap(t *testing.T) {
	_, err := ParseAndReturnNameGroupValueMap(SQLITE3_CREATE_TABLE_FIRST_PASS_PARSER, TEST_SQLITE3_CREATE_STATEMENT_A)

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

}

func TestFirstPassParseCreateStatement(t *testing.T) {
	_, _, err := firstPassParseCreateStatement(TEST_SQLITE3_CREATE_STATEMENT_A)
	if err != nil {
		t.Error(err)
	}

}

func TestSecondPassParseCreateStatement(t *testing.T) {
	_, columns, err := firstPassParseCreateStatement(sqlconstants.SQLITE3_CREATE_BUCKET_SCHEMA)
	if err != nil {
		t.Error(err)
	}

	_, err = secondPassParseCreateStatement(columns)

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

	_, columns, err = firstPassParseCreateStatement(TEST_SQLITE3_CREATE_STATEMENT_B)
	if err != nil {
		t.Error(err)
	}

	_, err = secondPassParseCreateStatement(columns)

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

	_, columns, err = firstPassParseCreateStatement(TEST_SQLITE3_CREATE_STATEMENT_C)
	if err != nil {
		t.Error(err)
	}

	_, err = secondPassParseCreateStatement(columns)

	if err == nil {
		t.Error("Error should have been caught")
	}

}

func TestParseCreateStatement(t *testing.T) {

	//CASE A

	table, columnArrayMap, err := ParseCreateStatement(TEST_SQLITE3_CREATE_STATEMENT_A)

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

	if table != "FOO" {
		t.Error("error table name is incorrect")
	}

	cam := []map[string]string{}
	c1 := map[string]string{"": "X INTEGER", "columntype": "INTEGER", "columnname": "X", "constraints": ""}
	c2 := map[string]string{"": "Y TEXT PRIMARY KEY", "columntype": "TEXT", "columnname": "Y", "constraints": "PRIMARY KEY"}

	cam = append(cam, c1)
	cam = append(cam, c2)

	if !reflect.DeepEqual(columnArrayMap, cam) {
		t.Error("ParseCreateStatement created unexpected results => test case ->", TEST_SQLITE3_CREATE_STATEMENT_A)
		t.Error("test data =>", cam)
		t.Error("returned data =>", columnArrayMap)
	}

	//CASE B

	table, columnArrayMap, err = ParseCreateStatement(TEST_SQLITE3_CREATE_STATEMENT_B)

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

	if table != "FOO" {
		t.Error("error table name is incorrect")
	}

	cam = []map[string]string{}
	c1 = map[string]string{"": "X INTEGER", "columntype": "INTEGER", "columnname": "X", "constraints": ""}
	c2 = map[string]string{"": "Y TEXT PRIMARY KEY", "columntype": "TEXT", "columnname": "Y", "constraints": "PRIMARY KEY"}
	c3 := map[string]string{"": "Z BETA NOT NULL", "columntype": "BETA", "columnname": "Z", "constraints": "NOT NULL"}

	cam = append(cam, c1)
	cam = append(cam, c2)
	cam = append(cam, c3)

	if !reflect.DeepEqual(columnArrayMap, cam) {
		t.Error("ParseCreateStatement created unexpected results => test case ->", TEST_SQLITE3_CREATE_STATEMENT_B)
		t.Error("test data =>", cam)
		t.Error("returned data =>", columnArrayMap)
	}

	//CASE C

	table, columnArrayMap, err = ParseCreateStatement(TEST_SQLITE3_CREATE_STATEMENT_C)

	if err == nil {
		t.Error("ERROR should be thrown")
	}

	//CASE D
	table, columnArrayMap, err = ParseCreateStatement(TEST_SQLITE3_CREATE_STATEMENT_D)

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

	if table != "FOO" {
		t.Error("error table name is incorrect")
	}

	cam = []map[string]string{}
	c1 = map[string]string{"": "X INTEGER", "columntype": "INTEGER", "columnname": "X", "constraints": ""}
	c2 = map[string]string{"": "Y TEXT PRIMARY KEY", "columntype": "TEXT", "columnname": "Y", "constraints": "PRIMARY KEY"}
	c3 = map[string]string{"": "Z BETA NOT NULL", "columntype": "BETA", "columnname": "Z", "constraints": "NOT NULL"}
	c4 := map[string]string{"": "T DEFAULT  CURRENT_TIMESTAMP", "columntype": "TIMESTAMP", "columnname": "T", "constraints": "DEFAULT  CURRENT_TIMESTAMP"}

	cam = append(cam, c1)
	cam = append(cam, c2)
	cam = append(cam, c3)
	cam = append(cam, c4)

	if !reflect.DeepEqual(columnArrayMap, cam) {
		t.Error("ParseCreateStatement created unexpected results => test case ->", TEST_SQLITE3_CREATE_STATEMENT_D)
		t.Error("test data =>", cam)
		t.Error("returned data =>", columnArrayMap)
	}

}
<|endoftext|>


<commit_before>// Copyright 2011 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.

// This file implements CGI from the perspective of a child
// process.

package cgi

import (
	"bufio"
	"crypto/tls"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"strings"
)

// Request returns the HTTP request as represented in the current
// environment. This assumes the current program is being run
// by a web server in a CGI environment.
// The returned Request's Body is populated, if applicable.
func Request() (*http.Request, error) {
	r, err := RequestFromMap(envMap(os.Environ()))
	if err != nil {
		return nil, err
	}
	if r.ContentLength > 0 {
		r.Body = ioutil.NopCloser(io.LimitReader(os.Stdin, r.ContentLength))
	}
	return r, nil
}

func envMap(env []string) map[string]string {
	m := make(map[string]string)
	for _, kv := range env {
		if idx := strings.Index(kv, "="); idx != -1 {
			m[kv[:idx]] = kv[idx+1:]
		}
	}
	return m
}

// RequestFromMap creates an http.Request from CGI variables.
// The returned Request's Body field is not populated.
func RequestFromMap(params map[string]string) (*http.Request, error) {
	r := new(http.Request)
	r.Method = params["REQUEST_METHOD"]
	if r.Method == "" {
		return nil, errors.New("cgi: no REQUEST_METHOD in environment")
	}

	r.Proto = params["SERVER_PROTOCOL"]
	var ok bool
	r.ProtoMajor, r.ProtoMinor, ok = http.ParseHTTPVersion(r.Proto)
	if !ok {
		return nil, errors.New("cgi: invalid SERVER_PROTOCOL version")
	}

	r.Close = true
	r.Trailer = http.Header{}
	r.Header = http.Header{}

	r.Host = params["HTTP_HOST"]

	if lenstr := params["CONTENT_LENGTH"]; lenstr != "" {
		clen, err := strconv.ParseInt(lenstr, 10, 64)
		if err != nil {
			return nil, errors.New("cgi: bad CONTENT_LENGTH in environment: " + lenstr)
		}
		r.ContentLength = clen
	}

	if ct := params["CONTENT_TYPE"]; ct != "" {
		r.Header.Set("Content-Type", ct)
	}

	// Copy "HTTP_FOO_BAR" variables to "Foo-Bar" Headers
	for k, v := range params {
		if !strings.HasPrefix(k, "HTTP_") || k == "HTTP_HOST" {
			continue
		}
		r.Header.Add(strings.ReplaceAll(k[5:], "_", "-"), v)
	}

	// TODO: cookies.  parsing them isn't exported, though.

	uriStr := params["REQUEST_URI"]
	if uriStr == "" {
		// Fallback to SCRIPT_NAME, PATH_INFO and QUERY_STRING.
		uriStr = params["SCRIPT_NAME"] + params["PATH_INFO"]
		s := params["QUERY_STRING"]
		if s != "" {
			uriStr += "?" + s
		}
	}

	// There's apparently a de-facto standard for this.
	// https://docstore.mik.ua/orelly/linux/cgi/ch03_02.htm#ch03-35636
	if s := params["HTTPS"]; s == "on" || s == "ON" || s == "1" {
		r.TLS = &tls.ConnectionState{HandshakeComplete: true}
	}

	if r.Host != "" {
		// Hostname is provided, so we can reasonably construct a URL.
		rawurl := r.Host + uriStr
		if r.TLS == nil {
			rawurl = "http://" + rawurl
		} else {
			rawurl = "https://" + rawurl
		}
		url, err := url.Parse(rawurl)
		if err != nil {
			return nil, errors.New("cgi: failed to parse host and REQUEST_URI into a URL: " + rawurl)
		}
		r.URL = url
	}
	// Fallback logic if we don't have a Host header or the URL
	// failed to parse
	if r.URL == nil {
		url, err := url.Parse(uriStr)
		if err != nil {
			return nil, errors.New("cgi: failed to parse REQUEST_URI into a URL: " + uriStr)
		}
		r.URL = url
	}

	// Request.RemoteAddr has its port set by Go's standard http
	// server, so we do here too.
	remotePort, _ := strconv.Atoi(params["REMOTE_PORT"]) // zero if unset or invalid
	r.RemoteAddr = net.JoinHostPort(params["REMOTE_ADDR"], strconv.Itoa(remotePort))

	return r, nil
}

// Serve executes the provided Handler on the currently active CGI
// request, if any. If there's no current CGI environment
// an error is returned. The provided handler may be nil to use
// http.DefaultServeMux.
func Serve(handler http.Handler) error {
	req, err := Request()
	if err != nil {
		return err
	}
	if handler == nil {
		handler = http.DefaultServeMux
	}
	rw := &response{
		req:    req,
		header: make(http.Header),
		bufw:   bufio.NewWriter(os.Stdout),
	}
	handler.ServeHTTP(rw, req)
	rw.Write(nil) // make sure a response is sent
	if err = rw.bufw.Flush(); err != nil {
		return err
	}
	return nil
}

type response struct {
	req        *http.Request
	header     http.Header
	bufw       *bufio.Writer
	headerSent bool
}

func (r *response) Flush() {
	r.bufw.Flush()
}

func (r *response) Header() http.Header {
	return r.header
}

func (r *response) Write(p []byte) (n int, err error) {
	if !r.headerSent {
		r.WriteHeader(http.StatusOK)
	}
	return r.bufw.Write(p)
}

func (r *response) WriteHeader(code int) {
	if r.headerSent {
		// Note: explicitly using Stderr, as Stdout is our HTTP output.
		fmt.Fprintf(os.Stderr, "CGI attempted to write header twice on request for %s", r.req.URL)
		return
	}
	r.headerSent = true
	fmt.Fprintf(r.bufw, "Status: %d %s\r\n", code, http.StatusText(code))

	// Set a default Content-Type
	if _, hasType := r.header["Content-Type"]; !hasType {
		r.header.Add("Content-Type", "text/html; charset=utf-8")
	}

	r.header.Write(r.bufw)
	r.bufw.WriteString("\r\n")
	r.bufw.Flush()
}
<commit_msg>net/http: update net/http package to replace a broken link with an archive link<commit_after>// Copyright 2011 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.

// This file implements CGI from the perspective of a child
// process.

package cgi

import (
	"bufio"
	"crypto/tls"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"strings"
)

// Request returns the HTTP request as represented in the current
// environment. This assumes the current program is being run
// by a web server in a CGI environment.
// The returned Request's Body is populated, if applicable.
func Request() (*http.Request, error) {
	r, err := RequestFromMap(envMap(os.Environ()))
	if err != nil {
		return nil, err
	}
	if r.ContentLength > 0 {
		r.Body = ioutil.NopCloser(io.LimitReader(os.Stdin, r.ContentLength))
	}
	return r, nil
}

func envMap(env []string) map[string]string {
	m := make(map[string]string)
	for _, kv := range env {
		if idx := strings.Index(kv, "="); idx != -1 {
			m[kv[:idx]] = kv[idx+1:]
		}
	}
	return m
}

// RequestFromMap creates an http.Request from CGI variables.
// The returned Request's Body field is not populated.
func RequestFromMap(params map[string]string) (*http.Request, error) {
	r := new(http.Request)
	r.Method = params["REQUEST_METHOD"]
	if r.Method == "" {
		return nil, errors.New("cgi: no REQUEST_METHOD in environment")
	}

	r.Proto = params["SERVER_PROTOCOL"]
	var ok bool
	r.ProtoMajor, r.ProtoMinor, ok = http.ParseHTTPVersion(r.Proto)
	if !ok {
		return nil, errors.New("cgi: invalid SERVER_PROTOCOL version")
	}

	r.Close = true
	r.Trailer = http.Header{}
	r.Header = http.Header{}

	r.Host = params["HTTP_HOST"]

	if lenstr := params["CONTENT_LENGTH"]; lenstr != "" {
		clen, err := strconv.ParseInt(lenstr, 10, 64)
		if err != nil {
			return nil, errors.New("cgi: bad CONTENT_LENGTH in environment: " + lenstr)
		}
		r.ContentLength = clen
	}

	if ct := params["CONTENT_TYPE"]; ct != "" {
		r.Header.Set("Content-Type", ct)
	}

	// Copy "HTTP_FOO_BAR" variables to "Foo-Bar" Headers
	for k, v := range params {
		if !strings.HasPrefix(k, "HTTP_") || k == "HTTP_HOST" {
			continue
		}
		r.Header.Add(strings.ReplaceAll(k[5:], "_", "-"), v)
	}

	// TODO: cookies.  parsing them isn't exported, though.

	uriStr := params["REQUEST_URI"]
	if uriStr == "" {
		// Fallback to SCRIPT_NAME, PATH_INFO and QUERY_STRING.
		uriStr = params["SCRIPT_NAME"] + params["PATH_INFO"]
		s := params["QUERY_STRING"]
		if s != "" {
			uriStr += "?" + s
		}
	}

	// There's apparently a de-facto standard for this.
	// https://web.archive.org/web/20170105004655/http://docstore.mik.ua/orelly/linux/cgi/ch03_02.htm#ch03-35636
	if s := params["HTTPS"]; s == "on" || s == "ON" || s == "1" {
		r.TLS = &tls.ConnectionState{HandshakeComplete: true}
	}

	if r.Host != "" {
		// Hostname is provided, so we can reasonably construct a URL.
		rawurl := r.Host + uriStr
		if r.TLS == nil {
			rawurl = "http://" + rawurl
		} else {
			rawurl = "https://" + rawurl
		}
		url, err := url.Parse(rawurl)
		if err != nil {
			return nil, errors.New("cgi: failed to parse host and REQUEST_URI into a URL: " + rawurl)
		}
		r.URL = url
	}
	// Fallback logic if we don't have a Host header or the URL
	// failed to parse
	if r.URL == nil {
		url, err := url.Parse(uriStr)
		if err != nil {
			return nil, errors.New("cgi: failed to parse REQUEST_URI into a URL: " + uriStr)
		}
		r.URL = url
	}

	// Request.RemoteAddr has its port set by Go's standard http
	// server, so we do here too.
	remotePort, _ := strconv.Atoi(params["REMOTE_PORT"]) // zero if unset or invalid
	r.RemoteAddr = net.JoinHostPort(params["REMOTE_ADDR"], strconv.Itoa(remotePort))

	return r, nil
}

// Serve executes the provided Handler on the currently active CGI
// request, if any. If there's no current CGI environment
// an error is returned. The provided handler may be nil to use
// http.DefaultServeMux.
func Serve(handler http.Handler) error {
	req, err := Request()
	if err != nil {
		return err
	}
	if handler == nil {
		handler = http.DefaultServeMux
	}
	rw := &response{
		req:    req,
		header: make(http.Header),
		bufw:   bufio.NewWriter(os.Stdout),
	}
	handler.ServeHTTP(rw, req)
	rw.Write(nil) // make sure a response is sent
	if err = rw.bufw.Flush(); err != nil {
		return err
	}
	return nil
}

type response struct {
	req        *http.Request
	header     http.Header
	bufw       *bufio.Writer
	headerSent bool
}

func (r *response) Flush() {
	r.bufw.Flush()
}

func (r *response) Header() http.Header {
	return r.header
}

func (r *response) Write(p []byte) (n int, err error) {
	if !r.headerSent {
		r.WriteHeader(http.StatusOK)
	}
	return r.bufw.Write(p)
}

func (r *response) WriteHeader(code int) {
	if r.headerSent {
		// Note: explicitly using Stderr, as Stdout is our HTTP output.
		fmt.Fprintf(os.Stderr, "CGI attempted to write header twice on request for %s", r.req.URL)
		return
	}
	r.headerSent = true
	fmt.Fprintf(r.bufw, "Status: %d %s\r\n", code, http.StatusText(code))

	// Set a default Content-Type
	if _, hasType := r.header["Content-Type"]; !hasType {
		r.header.Add("Content-Type", "text/html; charset=utf-8")
	}

	r.header.Write(r.bufw)
	r.bufw.WriteString("\r\n")
	r.bufw.Flush()
}
<|endoftext|>


<commit_before>// The test currently require two stela instances to be running
// One at the default setting > stela
// Another on port 9001 > stela -port 9001

package api

import (
	"testing"
	"time"

	"golang.org/x/net/context"

	"gitlab.fg/go/stela"
)

// func TestMain(m *testing.M) {
// 	kill, err := startStelaInstance(stela.DefaultStelaPort, stela.DefaultMulticastPort)
// 	if err != nil {
// 		log.Fatal(err)
// 	}

// 	t := m.Run()

// 	kill()
// 	os.Exit(t)
// }

func TestRegisterAndDiscover(t *testing.T) {
	// Create a second stela instance
	// kill, err := startStelaInstance(9001, stela.DefaultMulticastPort)
	// if err != nil {
	// 	t.Fatal(err)
	// }
	// defer kill()
	ctx := context.Background()

	serviceName := "apitest.services.fg"
	c, err := NewClient(ctx, stela.DefaultStelaAddress, "../testdata/ca.pem")
	if err != nil {
		t.Fatal(err)
	}
	defer c.Close()

	c2, err := NewClient(ctx, "127.0.0.1:9001", "../testdata/ca.pem")
	if err != nil {
		t.Fatal(err)
	}
	defer c2.Close()

	// Register services with c2
	c2Services := []*stela.Service{
		&stela.Service{
			Name:    serviceName,
			Target:  "jlu.macbook",
			Address: "127.0.0.1",
			Port:    9001,
		},
		&stela.Service{
			Name:    "discoverall.services.fg",
			Target:  "jlu.macbook",
			Address: "127.0.0.1",
			Port:    9001,
		},
		&stela.Service{
			Name:    stela.ServiceName,
			Target:  "jlu.macbook",
			Address: "127.0.0.1",
			Port:    10001,
		},
		&stela.Service{
			Name:    stela.ServiceName,
			Target:  "jlu.macbook",
			Address: "127.0.0.1",
			Port:    10000,
		},
	}
	for _, s := range c2Services {
		if err := c2.RegisterService(s); err != nil {
			t.Fatal(err)
		}
	}

	var expectedServices []*stela.Service

	// Add c2Service to expected
	expectedServices = append(expectedServices, c2Services[0])

	var tests = []struct {
		service    *stela.Service
		shouldFail bool
	}{
		{&stela.Service{
			Name:    serviceName,
			Target:  "jlu.macbook",
			Address: "127.0.0.1",
			Port:    9000,
		}, false},
		// Don't allow duplicates
		{&stela.Service{
			Name:    serviceName,
			Target:  "jlu.macbook",
			Address: "127.0.0.1",
			Port:    9000,
		}, true},
		{&stela.Service{
			Name:    serviceName,
			Target:  "jlu.macbook",
			Address: "localhost",
			Port:    80,
		}, false},
		{&stela.Service{
			Name:    "",
			Target:  "",
			Address: "",
			Port:    0,
		}, true},
	}

	for i, test := range tests {
		if err := c.RegisterService(test.service); test.shouldFail != (err != nil) {
			t.Fatal(i, test, err)
		}

		// Store the successful services into our own expected map
		if !test.shouldFail {
			expectedServices = append([]*stela.Service{test.service}, expectedServices...)
		}
	}

	// Now see if we can discover them
	services, err := c.Discover(serviceName)
	if err != nil {
		t.Fatal(err)
	}

	if len(services) != 3 {
		t.Fatal("Discover failed", services, expectedServices)
	}

	// Compare the discovered services
	equalServices(t, services, expectedServices)

	// DiscoverAll should return expected plus 1
	da, err := c.DiscoverAll()
	if err != nil {
		t.Fatal(err)
	}

	// Remove 1 from c2Services add 2 for each stela instance running
	if len(da) != len(expectedServices)+len(c2Services)+1 {
		t.Fatal("DiscoverAll failed", da)
	}

	// DiscoverOne with c2
	s, err := c2.DiscoverOne(serviceName)
	if err != nil {
		t.Fatal(err)
	}
	if !s.Valid() {
		t.Fatal("c2 DiscoveOne was invalid")
	}

	// Register another stela with c
	if err := c.RegisterService(&stela.Service{
		Name:    stela.ServiceName,
		Target:  "jlu.macbook",
		Address: "127.0.0.1",
		Port:    10002,
	}); err != nil {
		t.Fatal(err)
	}

	// Discover all stela instances
	stelas, err := c2.Discover(stela.ServiceName)
	if err != nil {
		t.Fatal(err)
	}
	if len(stelas) != 5 {
		t.Fatal("stela discovery failed", stelas)
	}
}

func TestConnectSubscribe(t *testing.T) {
	serviceName := "testSubscribe.services.fg"
	ctx, cancel := context.WithTimeout(context.TODO(), time.Millisecond*100)

	// Connect to both instances
	c, err := NewClient(ctx, stela.DefaultStelaAddress, "../testdata/ca.pem")
	if err != nil {
		t.Fatal(err)
	}
	defer c.Close()

	c2, err := NewClient(ctx, "127.0.0.1:9001", "../testdata/ca.pem")
	if err != nil {
		t.Fatal(err)
	}
	defer c2.Close()

	// Test services for c
	var testServices = []*stela.Service{
		&stela.Service{
			Name:    serviceName,
			Target:  "jlu.macbook",
			Address: "127.0.0.1",
			Port:    9000,
		},
		&stela.Service{
			Name:    serviceName,
			Target:  "jlu.macbook",
			Address: "127.0.0.2",
			Port:    9001,
		},
		&stela.Service{
			Name:    serviceName,
			Target:  "jlu.macbook",
			Address: "127.0.0.3",
			Port:    9002,
		},
	}

	waitCh := make(chan struct{})
	var count int
	callback := func(s *stela.Service) {
		count++
		// Total test services for both clients
		if count == len(testServices)*2 {
			close(waitCh)
		}

		if s.Action != stela.RegisterAction {
			t.Fatal("Service should be register action")
		}
	}

	if err := c.Subscribe(serviceName, callback); err != nil {
		t.Fatal(err)
	}

	if err := c2.Subscribe(serviceName, callback); err != nil {
		t.Fatal(err)
	}

	for _, s := range testServices {
		if err := c.RegisterService(s); err != nil {
			t.Fatal(err)
		}
	}

	// Wait for either a timeout or all the subscribed services to be read
	select {
	case <-waitCh:
		break
	case <-ctx.Done():
		if ctx.Err() != nil {
			t.Fatal("TestConnectSubscribe timed out: ", ctx.Err())
		}
	}
	if err := c.Unsubscribe(serviceName); err != nil {
		t.Fatal(err)
	}

	// Verify the map is empty
	if c.callbacks[serviceName] != nil {
		t.Fatal("callbacks map should be empty after Unsubscribe", c.callbacks)
	}
	// time.Sleep(time.Millisecond * 500)
	cancel()
}

// equalServices takes two slices of stela.Service and make sure they are correct
func equalServices(t *testing.T, s1, s2 []*stela.Service) {
	// Make sure the services returned were the ones sent
	total := len(s1)
	for _, rs := range s2 {
		for _, ts := range s1 {
			if rs.Equal(ts) {
				total--
			}
		}
	}

	if total != 0 {
		t.Fatalf("Services returned did not match services in slice")
	}
}

// func startStelaInstance(stelaPort, multicastPort int) (kill func(), err error) {
// 	// Run a stela instance
// 	cmd := exec.Command("stela", "-port", fmt.Sprint(stelaPort), "-multicast", fmt.Sprint(multicastPort))
// 	if err := cmd.Start(); err != nil {
// 		return nil, err
// 	}

// 	// Give the instance time to start
// 	time.Sleep(time.Millisecond * 100)

// 	return func() {
// 		if err := cmd.Process.Kill(); err != nil {
// 			fmt.Println("failed to kill: ", err)
// 		}
// 	}, nil
// }
<commit_msg>Changing api test to make sure c2 receives c's registered services<commit_after>// The test currently require two stela instances to be running
// One at the default setting > stela
// Another on port 9001 > stela -port 9001

package api

import (
	"testing"
	"time"

	"golang.org/x/net/context"

	"gitlab.fg/go/stela"
)

// func TestMain(m *testing.M) {
// 	kill, err := startStelaInstance(stela.DefaultStelaPort, stela.DefaultMulticastPort)
// 	if err != nil {
// 		log.Fatal(err)
// 	}

// 	t := m.Run()

// 	kill()
// 	os.Exit(t)
// }

func TestRegisterAndDiscover(t *testing.T) {
	// Create a second stela instance
	// kill, err := startStelaInstance(9001, stela.DefaultMulticastPort)
	// if err != nil {
	// 	t.Fatal(err)
	// }
	// defer kill()
	ctx := context.Background()

	serviceName := "apitest.services.fg"
	c, err := NewClient(ctx, stela.DefaultStelaAddress, "../testdata/ca.pem")
	if err != nil {
		t.Fatal(err)
	}
	defer c.Close()

	c2, err := NewClient(ctx, "127.0.0.1:9001", "../testdata/ca.pem")
	if err != nil {
		t.Fatal(err)
	}
	defer c2.Close()

	// Register services with c2
	c2Services := []*stela.Service{
		&stela.Service{
			Name:    serviceName,
			Target:  "jlu.macbook",
			Address: "127.0.0.1",
			Port:    9001,
		},
		&stela.Service{
			Name:    "discoverall.services.fg",
			Target:  "jlu.macbook",
			Address: "127.0.0.1",
			Port:    9001,
		},
		&stela.Service{
			Name:    stela.ServiceName,
			Target:  "jlu.macbook",
			Address: "127.0.0.1",
			Port:    10001,
		},
		&stela.Service{
			Name:    stela.ServiceName,
			Target:  "jlu.macbook",
			Address: "127.0.0.1",
			Port:    10000,
		},
	}
	for _, s := range c2Services {
		if err := c2.RegisterService(s); err != nil {
			t.Fatal(err)
		}
	}

	var expectedServices []*stela.Service

	// Add c2Service to expected
	expectedServices = append(expectedServices, c2Services[0])

	var tests = []struct {
		service    *stela.Service
		shouldFail bool
	}{
		{&stela.Service{
			Name:    serviceName,
			Target:  "jlu.macbook",
			Address: "127.0.0.1",
			Port:    9000,
		}, false},
		// Don't allow duplicates
		{&stela.Service{
			Name:    serviceName,
			Target:  "jlu.macbook",
			Address: "127.0.0.1",
			Port:    9000,
		}, true},
		{&stela.Service{
			Name:    serviceName,
			Target:  "jlu.macbook",
			Address: "localhost",
			Port:    80,
		}, false},
		{&stela.Service{
			Name:    "",
			Target:  "",
			Address: "",
			Port:    0,
		}, true},
	}

	for i, test := range tests {
		if err := c.RegisterService(test.service); test.shouldFail != (err != nil) {
			t.Fatal(i, test, err)
		}

		// Store the successful services into our own expected map
		if !test.shouldFail {
			expectedServices = append([]*stela.Service{test.service}, expectedServices...)
		}
	}

	// Now see if we can discover them
	services, err := c.Discover(serviceName)
	if err != nil {
		t.Fatal(err)
	}

	if len(services) != 3 {
		t.Fatal("Discover failed", services, expectedServices)
	}

	// Compare the discovered services
	equalServices(t, services, expectedServices)

	// DiscoverAll should return expected plus 1
	da, err := c.DiscoverAll()
	if err != nil {
		t.Fatal(err)
	}

	// Remove 1 from c2Services add 2 for each stela instance running
	if len(da) != len(expectedServices)+len(c2Services)+1 {
		t.Fatal("DiscoverAll failed", da)
	}

	// DiscoverOne with c2
	s, err := c2.DiscoverOne(serviceName)
	if err != nil {
		t.Fatal(err)
	}
	if !s.Valid() {
		t.Fatal("c2 DiscoveOne was invalid")
	}

	// Register another stela with c
	if err := c.RegisterService(&stela.Service{
		Name:    stela.ServiceName,
		Target:  "jlu.macbook",
		Address: "127.0.0.1",
		Port:    10002,
	}); err != nil {
		t.Fatal(err)
	}

	// Discover all stela instances
	stelas, err := c2.Discover(stela.ServiceName)
	if err != nil {
		t.Fatal(err)
	}
	if len(stelas) != 5 {
		t.Fatal("stela discovery failed", stelas)
	}
}

func TestConnectSubscribe(t *testing.T) {
	serviceName := "testSubscribe.services.fg"
	ctx, cancel := context.WithTimeout(context.TODO(), time.Millisecond*100)

	// Connect to both instances
	c, err := NewClient(ctx, stela.DefaultStelaAddress, "../testdata/ca.pem")
	if err != nil {
		t.Fatal(err)
	}
	defer c.Close()

	c2, err := NewClient(ctx, "127.0.0.1:9001", "../testdata/ca.pem")
	if err != nil {
		t.Fatal(err)
	}
	defer c2.Close()

	// Test services for c
	var testServices = []*stela.Service{
		&stela.Service{
			Name:    serviceName,
			Target:  "jlu.macbook",
			Address: "127.0.0.1",
			Port:    9000,
		},
		&stela.Service{
			Name:    serviceName,
			Target:  "jlu.macbook",
			Address: "127.0.0.2",
			Port:    9001,
		},
		&stela.Service{
			Name:    serviceName,
			Target:  "jlu.macbook",
			Address: "127.0.0.3",
			Port:    9002,
		},
	}

	waitCh := make(chan struct{})
	var count int
	callback := func(s *stela.Service) {
		count++
		// Test to make sure c2 receives all services registered with c
		if count == len(testServices) {
			close(waitCh)
		}

		if s.Action != stela.RegisterAction {
			t.Fatal("Service should be register action")
		}
	}

	// if err := c.Subscribe(serviceName, callback); err != nil {
	// 	t.Fatal(err)
	// }

	if err := c2.Subscribe(serviceName, callback); err != nil {
		t.Fatal(err)
	}

	for _, s := range testServices {
		if err := c.RegisterService(s); err != nil {
			t.Fatal(err)
		}
	}

	// Wait for either a timeout or all the subscribed services to be read
	select {
	case <-waitCh:
		break
	case <-ctx.Done():
		if ctx.Err() != nil {
			t.Fatal("TestConnectSubscribe timed out: ", ctx.Err())
		}
	}
	if err := c2.Unsubscribe(serviceName); err != nil {
		t.Fatal(err)
	}

	// Verify the map is empty
	if c.callbacks[serviceName] != nil {
		t.Fatal("callbacks map should be empty after Unsubscribe", c.callbacks)
	}
	// time.Sleep(time.Millisecond * 500)
	cancel()
}

// equalServices takes two slices of stela.Service and make sure they are correct
func equalServices(t *testing.T, s1, s2 []*stela.Service) {
	// Make sure the services returned were the ones sent
	total := len(s1)
	for _, rs := range s2 {
		for _, ts := range s1 {
			if rs.Equal(ts) {
				total--
			}
		}
	}

	if total != 0 {
		t.Fatalf("Services returned did not match services in slice")
	}
}

// func startStelaInstance(stelaPort, multicastPort int) (kill func(), err error) {
// 	// Run a stela instance
// 	cmd := exec.Command("stela", "-port", fmt.Sprint(stelaPort), "-multicast", fmt.Sprint(multicastPort))
// 	if err := cmd.Start(); err != nil {
// 		return nil, err
// 	}

// 	// Give the instance time to start
// 	time.Sleep(time.Millisecond * 100)

// 	return func() {
// 		if err := cmd.Process.Kill(); err != nil {
// 			fmt.Println("failed to kill: ", err)
// 		}
// 	}, nil
// }
<|endoftext|>


<commit_before>package api

import (
	"testing"
)

func TestValidateConfig(t *testing.T) {

	if New().validateConfig().Error() != "Modulepath must be set before starting the API server" {
		t.Error("validateConfig should return an error when the modulepath hasn't been set")
	}

	subject := New()
	subject.Config["modulepath"] = "stub modulepath"
	if subject.validateConfig() != nil {
		t.Error("validateConfig should return nil when the modulepath has been set")
	}
}
<commit_msg>Add test for fileurl input validation<commit_after>package api

import (
	"testing"
)

func TestValidateConfig(t *testing.T) {

	if New().validateConfig().Error() != "Modulepath must be set before starting the API server" {
		t.Error("validateConfig should return an error when the modulepath hasn't been set")
	}

	subject1 := New()
	subject1.Config["modulepath"] = "stub modulepath"
	subject1.Config["fileurl"] = "stub fileurl"
	if subject1.validateConfig() != nil {
		t.Error("validateConfig should return nil when the modulepath and fileurl have been set")
	}

	subject2 := New()
	subject2.Config["modulepath"] = "stub modulepath"
	if subject2.validateConfig().Error() != "Fileurl must be set before starting the API server" {
		t.Error("validateConfig should return an error when the fileurl hasn't been set")
	}

}
<|endoftext|>


<commit_before>/*
 *
 * k6 - a next-generation load testing tool
 * Copyright (C) 2016 Load Impact
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package js

import (
	"context"
	"fmt"
	"github.com/loadimpact/k6/lib"
	"github.com/stretchr/testify/assert"
	"testing"
	"time"
)

func TestSleep(t *testing.T) {
	if testing.Short() {
		return
	}

	testdata := map[string]struct {
		src string
		min time.Duration
	}{
		"float,sub-1s": {`0.2`, 200 * time.Millisecond},
		"float":        {`1.0`, 1 * time.Second},
		"int":          {`1`, 1 * time.Second},
	}
	for name, data := range testdata {
		t.Run(name, func(t *testing.T) {
			r, err := newSnippetRunner(fmt.Sprintf(`
			import { sleep } from "k6";
			export default function() {
				sleep(%s);
			}`, data.src))
			assert.NoError(t, err)

			vu, err := r.NewVU()
			assert.NoError(t, err)

			start := time.Now()

			_, err = vu.RunOnce(context.Background())
			assert.NoError(t, err)
			assert.True(t, time.Since(start) > data.min, "ran too short")
			assert.True(t, time.Since(start) < data.min+1*time.Second, "ran too long")
		})
	}
}

func TestDoGroup(t *testing.T) {
	if testing.Short() {
		return
	}

	r, err := newSnippetRunner(`
	import { group } from "k6";
	export default function() {
		group("test", fn);
	}`)
	assert.NoError(t, err)

	vu_, err := r.NewVU()
	assert.NoError(t, err)
	vu := vu_.(*VU)

	assert.NoError(t, vu.vm.Set("fn", func() {
		assert.Equal(t, "test", vu.group.Name)
	}))

	_, err = vu.RunOnce(context.Background())
	assert.NoError(t, err)
}

func TestDoGroupNested(t *testing.T) {
	if testing.Short() {
		return
	}

	r, err := newSnippetRunner(`
	import { group } from "k6";
	export default function() {
		group("outer", function() {
			group("inner", fn);
		});
	}`)
	assert.NoError(t, err)

	vu_, err := r.NewVU()
	assert.NoError(t, err)
	vu := vu_.(*VU)

	assert.NoError(t, vu.vm.Set("fn", func() {
		assert.Equal(t, "inner", vu.group.Name)
		assert.Equal(t, "outer", vu.group.Parent.Name)
	}))

	_, err = vu.RunOnce(context.Background())
	assert.NoError(t, err)
}

func TestDoGroupReturn(t *testing.T) {
	if testing.Short() {
		return
	}

	r, err := newSnippetRunner(`
	import { group, _assert } from "k6";
	export default function() {
		let v = group("group", function() {
			return 12345;
		});
		_assert(v === 12345);
	}`)
	assert.NoError(t, err)

	vu, err := r.NewVU()
	assert.NoError(t, err)
	_, err = vu.RunOnce(context.Background())
	assert.NoError(t, err)
}

func TestDoGroupReturnTrueByDefault(t *testing.T) {
	if testing.Short() {
		return
	}

	r, err := newSnippetRunner(`
	import { group, _assert } from "k6";
	export default function() {
		let v = group("group", function() {
			// no return
		});
		_assert(v === true);
	}`)
	assert.NoError(t, err)

	vu, err := r.NewVU()
	assert.NoError(t, err)
	_, err = vu.RunOnce(context.Background())
	assert.NoError(t, err)
}

func TestDoCheck(t *testing.T) {
	if testing.Short() {
		return
	}

	r, err := newSnippetRunner(`
	import { check } from "k6";
	export default function() {
		check(3, { "v === 3": (v) => v === 3 });
	}`)
	assert.NoError(t, err)

	vu_, err := r.NewVU()
	assert.NoError(t, err)
	vu := vu_.(*VU)

	_, err = vu.RunOnce(context.Background())
	assert.NoError(t, err)

	c := r.DefaultGroup.Checks["v === 3"]
	assert.NotNil(t, c)
	assert.Equal(t, "v === 3", c.Name)
	assert.Equal(t, r.DefaultGroup, c.Group)
	assert.Equal(t, int64(1), c.Passes)
	assert.Equal(t, int64(0), c.Fails)
}

func TestCheckInGroup(t *testing.T) {
	if testing.Short() {
		return
	}

	r, err := newSnippetRunner(`
	import { group, check } from "k6";
	export default function() {
		group("group", function() {
			check(3, { "v === 3": (v) => v === 3 });
		});
	}`)
	assert.NoError(t, err)

	vu_, err := r.NewVU()
	assert.NoError(t, err)
	vu := vu_.(*VU)

	_, err = vu.RunOnce(context.Background())
	assert.NoError(t, err)

	g := r.DefaultGroup.Groups["group"]
	assert.NotNil(t, g)
	assert.Equal(t, "group", g.Name)

	c := g.Checks["v === 3"]
	assert.NotNil(t, c)
	assert.Equal(t, "v === 3", c.Name)
	assert.Equal(t, g, c.Group)
	assert.Equal(t, int64(1), c.Passes)
	assert.Equal(t, int64(0), c.Fails)
}

func TestCheckReturnTrueOnSuccess(t *testing.T) {
	if testing.Short() {
		return
	}

	r, err := newSnippetRunner(`
	import { check, _assert } from "k6";
	export default function() {
		let succ = check(null, { "true": true });
		_assert(succ === true);
	}`)
	assert.NoError(t, err)

	vu, err := r.NewVU()
	assert.NoError(t, err)
	_, err = vu.RunOnce(context.Background())
	assert.NoError(t, err)
}

func TestCheckReturnFalseAndTaintsOnFailure(t *testing.T) {
	if testing.Short() {
		return
	}

	r, err := newSnippetRunner(`
	import { check, _assert } from "k6";
	export default function() {
		let succ = check(null, { "false": false });
		_assert(succ === false);
	}`)
	assert.NoError(t, err)

	vu, err := r.NewVU()
	assert.NoError(t, err)
	_, err = vu.RunOnce(context.Background())
	assert.Equal(t, lib.ErrVUWantsTaint, err)
}

func TestTaint(t *testing.T) {
	if testing.Short() {
		return
	}

	r, err := newSnippetRunner(`
	import { taint } from "k6";
	export default function() {
		taint();
	}`)
	assert.NoError(t, err)

	vu, err := r.NewVU()
	assert.NoError(t, err)

	_, err = vu.RunOnce(context.Background())
	assert.Equal(t, lib.ErrVUWantsTaint, err)
}
<commit_msg>[change] You can no longer sleep() past test end<commit_after>/*
 *
 * k6 - a next-generation load testing tool
 * Copyright (C) 2016 Load Impact
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package js

import (
	"context"
	"fmt"
	"github.com/loadimpact/k6/lib"
	"github.com/stretchr/testify/assert"
	"testing"
	"time"
)

func TestSleep(t *testing.T) {
	if testing.Short() {
		return
	}

	testdata := map[string]struct {
		src string
		min time.Duration
	}{
		"float,sub-1s": {`0.2`, 200 * time.Millisecond},
		"float":        {`1.0`, 1 * time.Second},
		"int":          {`1`, 1 * time.Second},
		"exceeding":    {`5`, 2 * time.Second},
	}
	for name, data := range testdata {
		t.Run(name, func(t *testing.T) {
			r, err := newSnippetRunner(fmt.Sprintf(`
			import { sleep } from "k6";
			export default function() {
				sleep(%s);
			}`, data.src))
			assert.NoError(t, err)

			vu, err := r.NewVU()
			assert.NoError(t, err)

			ctx, _ := context.WithTimeout(context.Background(), 2*time.Second)
			start := time.Now()

			_, err = vu.RunOnce(ctx)
			assert.NoError(t, err)
			assert.True(t, time.Since(start) > data.min, "ran too short")
			assert.True(t, time.Since(start) < data.min+1*time.Second, "ran too long")
		})
	}
}

func TestDoGroup(t *testing.T) {
	if testing.Short() {
		return
	}

	r, err := newSnippetRunner(`
	import { group } from "k6";
	export default function() {
		group("test", fn);
	}`)
	assert.NoError(t, err)

	vu_, err := r.NewVU()
	assert.NoError(t, err)
	vu := vu_.(*VU)

	assert.NoError(t, vu.vm.Set("fn", func() {
		assert.Equal(t, "test", vu.group.Name)
	}))

	_, err = vu.RunOnce(context.Background())
	assert.NoError(t, err)
}

func TestDoGroupNested(t *testing.T) {
	if testing.Short() {
		return
	}

	r, err := newSnippetRunner(`
	import { group } from "k6";
	export default function() {
		group("outer", function() {
			group("inner", fn);
		});
	}`)
	assert.NoError(t, err)

	vu_, err := r.NewVU()
	assert.NoError(t, err)
	vu := vu_.(*VU)

	assert.NoError(t, vu.vm.Set("fn", func() {
		assert.Equal(t, "inner", vu.group.Name)
		assert.Equal(t, "outer", vu.group.Parent.Name)
	}))

	_, err = vu.RunOnce(context.Background())
	assert.NoError(t, err)
}

func TestDoGroupReturn(t *testing.T) {
	if testing.Short() {
		return
	}

	r, err := newSnippetRunner(`
	import { group, _assert } from "k6";
	export default function() {
		let v = group("group", function() {
			return 12345;
		});
		_assert(v === 12345);
	}`)
	assert.NoError(t, err)

	vu, err := r.NewVU()
	assert.NoError(t, err)
	_, err = vu.RunOnce(context.Background())
	assert.NoError(t, err)
}

func TestDoGroupReturnTrueByDefault(t *testing.T) {
	if testing.Short() {
		return
	}

	r, err := newSnippetRunner(`
	import { group, _assert } from "k6";
	export default function() {
		let v = group("group", function() {
			// no return
		});
		_assert(v === true);
	}`)
	assert.NoError(t, err)

	vu, err := r.NewVU()
	assert.NoError(t, err)
	_, err = vu.RunOnce(context.Background())
	assert.NoError(t, err)
}

func TestDoCheck(t *testing.T) {
	if testing.Short() {
		return
	}

	r, err := newSnippetRunner(`
	import { check } from "k6";
	export default function() {
		check(3, { "v === 3": (v) => v === 3 });
	}`)
	assert.NoError(t, err)

	vu_, err := r.NewVU()
	assert.NoError(t, err)
	vu := vu_.(*VU)

	_, err = vu.RunOnce(context.Background())
	assert.NoError(t, err)

	c := r.DefaultGroup.Checks["v === 3"]
	assert.NotNil(t, c)
	assert.Equal(t, "v === 3", c.Name)
	assert.Equal(t, r.DefaultGroup, c.Group)
	assert.Equal(t, int64(1), c.Passes)
	assert.Equal(t, int64(0), c.Fails)
}

func TestCheckInGroup(t *testing.T) {
	if testing.Short() {
		return
	}

	r, err := newSnippetRunner(`
	import { group, check } from "k6";
	export default function() {
		group("group", function() {
			check(3, { "v === 3": (v) => v === 3 });
		});
	}`)
	assert.NoError(t, err)

	vu_, err := r.NewVU()
	assert.NoError(t, err)
	vu := vu_.(*VU)

	_, err = vu.RunOnce(context.Background())
	assert.NoError(t, err)

	g := r.DefaultGroup.Groups["group"]
	assert.NotNil(t, g)
	assert.Equal(t, "group", g.Name)

	c := g.Checks["v === 3"]
	assert.NotNil(t, c)
	assert.Equal(t, "v === 3", c.Name)
	assert.Equal(t, g, c.Group)
	assert.Equal(t, int64(1), c.Passes)
	assert.Equal(t, int64(0), c.Fails)
}

func TestCheckReturnTrueOnSuccess(t *testing.T) {
	if testing.Short() {
		return
	}

	r, err := newSnippetRunner(`
	import { check, _assert } from "k6";
	export default function() {
		let succ = check(null, { "true": true });
		_assert(succ === true);
	}`)
	assert.NoError(t, err)

	vu, err := r.NewVU()
	assert.NoError(t, err)
	_, err = vu.RunOnce(context.Background())
	assert.NoError(t, err)
}

func TestCheckReturnFalseAndTaintsOnFailure(t *testing.T) {
	if testing.Short() {
		return
	}

	r, err := newSnippetRunner(`
	import { check, _assert } from "k6";
	export default function() {
		let succ = check(null, { "false": false });
		_assert(succ === false);
	}`)
	assert.NoError(t, err)

	vu, err := r.NewVU()
	assert.NoError(t, err)
	_, err = vu.RunOnce(context.Background())
	assert.Equal(t, lib.ErrVUWantsTaint, err)
}

func TestTaint(t *testing.T) {
	if testing.Short() {
		return
	}

	r, err := newSnippetRunner(`
	import { taint } from "k6";
	export default function() {
		taint();
	}`)
	assert.NoError(t, err)

	vu, err := r.NewVU()
	assert.NoError(t, err)

	_, err = vu.RunOnce(context.Background())
	assert.Equal(t, lib.ErrVUWantsTaint, err)
}
<|endoftext|>


<commit_before>//   Copyright 2017 MSolution.IO
//
//   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 routes

import (
	"context"
	"database/sql"
	"errors"
	"fmt"
	"net/http"

	"github.com/trackit/jsonlog"

	"github.com/trackit/trackit/aws"
	"github.com/trackit/trackit/db"
	"github.com/trackit/trackit/models"
	"github.com/trackit/trackit/routes"
	"github.com/trackit/trackit/users"
)

// postAwsAccountRequestBody is the expected request body for the
// postAwsAccount request handler.
type postAwsAccountRequestBody struct {
	RoleArn    string `json:"roleArn"  req:"nonzero"`
	External   string `json:"external" req:"nonzero"`
	Pretty     string `json:"pretty"`
	Payer      bool   `json:"payer"`
	TagbotUser bool   `json:"tagbotUser"`
}

var (
	errInvalidAccount     = errors.New("could not validate role and external ID")
	errFailCreateAccount  = errors.New("failed to create AWS account")
	errFailUpdateExternal = errors.New("failed to update external")
)

// postAwsAccount is a route handler which lets the user add AwsAccounts to
// their account.
func postAwsAccount(r *http.Request, a routes.Arguments) (int, interface{}) {
	var body postAwsAccountRequestBody
	routes.MustRequestBody(a, &body)
	tx := a[db.Transaction].(*sql.Tx)
	u := a[users.AuthenticatedUser].(users.User)
	return postAwsAccountWithValidBody(r, tx, u, body)
}

// postAwsAccountWithValidBody handles the logic of postAwsAccount assuming the
// request body is valid.
func postAwsAccountWithValidBody(r *http.Request, tx *sql.Tx, user users.User, body postAwsAccountRequestBody) (int, interface{}) {
	ctx := r.Context()
	logger := jsonlog.LoggerFromContextOrDefault(ctx)
	account := aws.AwsAccount{
		RoleArn:     body.RoleArn,
		External:    body.External,
		UserId:      user.Id,
		Pretty:      body.Pretty,
		Payer:       body.Payer,
		AwsIdentity: "",
	}
	if account.External != user.NextExternal {
		logger.Warning("tried to add AWS account with bad external", account)
		return 400, errors.New("incorrect external. Use /aws/next to get expected external")
	} else if err := testAndCreateAwsAccount(ctx, tx, &account, &user); err == nil {
		err = setNeedsTagbotOnboarding(tx, body, account)
		if err != nil {
			logger.Error("Could not set needs tagbot onboard flag.", map[string]interface{}{
				"account": account.Id,
				"err":     err.Error(),
			})
		}
		return 200, account
	} else {
		switch err {
		case errInvalidAccount:
			return 400, err
		default:
			return 500, err
		}
	}
}

// testAndCreateAwsAccount tests an AwsAccount can be assumed-role and then
// saves it to the database.
func testAndCreateAwsAccount(ctx context.Context, tx *sql.Tx, account *aws.AwsAccount, user *users.User) error {
	logger := jsonlog.LoggerFromContextOrDefault(ctx)
	if _, err := aws.GetTemporaryCredentials(*account, "validityTest"); err != nil {
		fmt.Print(err)
		return errInvalidAccount
	}
	if err := account.CreateAwsAccount(ctx, tx); err != nil {
		logger.Error("failed to insert AWS account", newTestAndCreateAwsAccountError(err, *account, *user))
		return errFailCreateAccount
	}
	user.NextExternal = ""
	if err := user.UpdateNextExternal(ctx, tx); err != nil {
		logger.Error("failed to update external", newTestAndCreateAwsAccountError(err, *account, *user))
		return errFailUpdateExternal
	}
	return nil
}

// testAndCreateAwsAccountError is used to log errors in
// testAndCreateAwsAccount.
type testAndCreateAwsAccountError struct {
	err     string         `json:"error"`
	account aws.AwsAccount `json:"account"`
	user    users.User     `json:"user"`
}

// newTestAndCreateAwsAccountError is used to log errors in
// testAndCreateAwsAccount.
func newTestAndCreateAwsAccountError(e error, a aws.AwsAccount, u users.User) testAndCreateAwsAccountError {
	return testAndCreateAwsAccountError{
		err:     e.Error(),
		account: a,
		user:    u,
	}
}

func setNeedsTagbotOnboarding(tx *sql.Tx, body postAwsAccountRequestBody, awsAccount aws.AwsAccount) error {
	if !body.TagbotUser {
		return nil
	}

	dbAwsAccount, err := models.AwsAccountByID(tx, awsAccount.Id)
	if err != nil {
		return err
	}

	dbAwsAccount.NeedsTagbotOnboarding = true

	return dbAwsAccount.Update(tx)
}
<commit_msg>fix: fields had json tags without being exported<commit_after>//   Copyright 2017 MSolution.IO
//
//   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 routes

import (
	"context"
	"database/sql"
	"errors"
	"fmt"
	"net/http"

	"github.com/trackit/jsonlog"

	"github.com/trackit/trackit/aws"
	"github.com/trackit/trackit/db"
	"github.com/trackit/trackit/models"
	"github.com/trackit/trackit/routes"
	"github.com/trackit/trackit/users"
)

// postAwsAccountRequestBody is the expected request body for the
// postAwsAccount request handler.
type postAwsAccountRequestBody struct {
	RoleArn    string `json:"roleArn"  req:"nonzero"`
	External   string `json:"external" req:"nonzero"`
	Pretty     string `json:"pretty"`
	Payer      bool   `json:"payer"`
	TagbotUser bool   `json:"tagbotUser"`
}

var (
	errInvalidAccount     = errors.New("could not validate role and external ID")
	errFailCreateAccount  = errors.New("failed to create AWS account")
	errFailUpdateExternal = errors.New("failed to update external")
)

// postAwsAccount is a route handler which lets the user add AwsAccounts to
// their account.
func postAwsAccount(r *http.Request, a routes.Arguments) (int, interface{}) {
	var body postAwsAccountRequestBody
	routes.MustRequestBody(a, &body)
	tx := a[db.Transaction].(*sql.Tx)
	u := a[users.AuthenticatedUser].(users.User)
	return postAwsAccountWithValidBody(r, tx, u, body)
}

// postAwsAccountWithValidBody handles the logic of postAwsAccount assuming the
// request body is valid.
func postAwsAccountWithValidBody(r *http.Request, tx *sql.Tx, user users.User, body postAwsAccountRequestBody) (int, interface{}) {
	ctx := r.Context()
	logger := jsonlog.LoggerFromContextOrDefault(ctx)
	account := aws.AwsAccount{
		RoleArn:     body.RoleArn,
		External:    body.External,
		UserId:      user.Id,
		Pretty:      body.Pretty,
		Payer:       body.Payer,
		AwsIdentity: "",
	}
	if account.External != user.NextExternal {
		logger.Warning("tried to add AWS account with bad external", account)
		return 400, errors.New("incorrect external. Use /aws/next to get expected external")
	} else if err := testAndCreateAwsAccount(ctx, tx, &account, &user); err == nil {
		err = setNeedsTagbotOnboarding(tx, body, account)
		if err != nil {
			logger.Error("Could not set needs tagbot onboard flag.", map[string]interface{}{
				"account": account.Id,
				"err":     err.Error(),
			})
		}
		return 200, account
	} else {
		switch err {
		case errInvalidAccount:
			return 400, err
		default:
			return 500, err
		}
	}
}

// testAndCreateAwsAccount tests an AwsAccount can be assumed-role and then
// saves it to the database.
func testAndCreateAwsAccount(ctx context.Context, tx *sql.Tx, account *aws.AwsAccount, user *users.User) error {
	logger := jsonlog.LoggerFromContextOrDefault(ctx)
	if _, err := aws.GetTemporaryCredentials(*account, "validityTest"); err != nil {
		fmt.Print(err)
		return errInvalidAccount
	}
	if err := account.CreateAwsAccount(ctx, tx); err != nil {
		logger.Error("failed to insert AWS account", newTestAndCreateAwsAccountError(err, *account, *user))
		return errFailCreateAccount
	}
	user.NextExternal = ""
	if err := user.UpdateNextExternal(ctx, tx); err != nil {
		logger.Error("failed to update external", newTestAndCreateAwsAccountError(err, *account, *user))
		return errFailUpdateExternal
	}
	return nil
}

// testAndCreateAwsAccountError is used to log errors in
// testAndCreateAwsAccount.
type testAndCreateAwsAccountError struct {
	Err     string         `json:"error"`
	Account aws.AwsAccount `json:"account"`
	User    users.User     `json:"user"`
}

// newTestAndCreateAwsAccountError is used to log errors in
// testAndCreateAwsAccount.
func newTestAndCreateAwsAccountError(e error, a aws.AwsAccount, u users.User) testAndCreateAwsAccountError {
	return testAndCreateAwsAccountError{
		Err:     e.Error(),
		Account: a,
		User:    u,
	}
}

func setNeedsTagbotOnboarding(tx *sql.Tx, body postAwsAccountRequestBody, awsAccount aws.AwsAccount) error {
	if !body.TagbotUser {
		return nil
	}

	dbAwsAccount, err := models.AwsAccountByID(tx, awsAccount.Id)
	if err != nil {
		return err
	}

	dbAwsAccount.NeedsTagbotOnboarding = true

	return dbAwsAccount.Update(tx)
}
<|endoftext|>


<commit_before>package logyard

import (
	"encoding/json"
	"github.com/ActiveState/doozer"
	"github.com/ActiveState/doozerconfig"
	"github.com/ActiveState/log"
)

type logyardConfig struct {
	Drains       map[string]string `doozer:"drains"`
	RetryLimits  map[string]string `doozer:"retrylimits"`
	DrainFormats map[string]string `doozer:"drainformats"`
	Doozer       *doozer.Conn
	Rev          int64                     // Doozer revision this config struct corresponds to
	Ch           chan *doozerconfig.Change // Doozer changes to the Drains map
}

// Logyard configuration object tied to doozer config
var Config *logyardConfig

const DOOZER_PREFIX = "/proc/logyard/config/"

var doozerCfg *doozerconfig.DoozerConfig

// Init initializes logyard with the given config entry point in
// doozer.
func Init(conn *doozer.Conn, rev int64, monitor bool) {
	Config = new(logyardConfig)
	Config.Drains = make(map[string]string)
	Config.RetryLimits = make(map[string]string)
	Config.DrainFormats = make(map[string]string)
	Config.Ch = make(chan *doozerconfig.Change)
	Config.Rev = rev
	Config.Doozer = conn
	doozerCfg = doozerconfig.New(conn, rev, Config, DOOZER_PREFIX)
	err := doozerCfg.Load()
	if err != nil {
		log.Fatal(err)
	}

	if !monitor {
		return
	}

	// Monitor drain config changes in doozer
	go doozerCfg.Monitor(DOOZER_PREFIX+"drains/*", func(change *doozerconfig.Change, err error) {
		if err != nil {
			// don't bring down the entire logyard, because we want
			// the existing drains to continue to function despite an
			// error in monitoring config changes.
			log.Errorf("Unable to process drain config change in doozer: %s", err)
			return
		}
		log.Infof("Detected change in doozer drains config: %s", change.Key)
		// Config.Rev = TODO
		if change.FieldName == "Drains" {
			switch change.Type {
			case doozerconfig.DELETE, doozerconfig.SET:
				Config.Ch <- change
			}
		}
	})
}

// DeleteDrain deletes the drain from doozer tree
func (Config *logyardConfig) DeleteDrain(name string) error {
	err := Config.Doozer.Del(DOOZER_PREFIX+"drains/"+name, -1)
	if err != nil {
		return err
	}
	return nil
}

// AddDrain adds the drain to the doozer tree
func (Config *logyardConfig) AddDrain(name string, uri string) error {
	data, err := json.Marshal(uri)
	if err != nil {
		return err
	}
	_, err = Config.Doozer.Set(DOOZER_PREFIX+"drains/"+name, Config.Rev, data)
	if err != nil {
		return err
	}
	return nil
}
<commit_msg>use ha doozer repos<commit_after>package logyard

import (
	"encoding/json"
	"github.com/ActiveState/doozerconfig"
	"github.com/ActiveState/log"
	"github.com/ha/doozer"
)

type logyardConfig struct {
	Drains       map[string]string `doozer:"drains"`
	RetryLimits  map[string]string `doozer:"retrylimits"`
	DrainFormats map[string]string `doozer:"drainformats"`
	Doozer       *doozer.Conn
	Rev          int64                     // Doozer revision this config struct corresponds to
	Ch           chan *doozerconfig.Change // Doozer changes to the Drains map
}

// Logyard configuration object tied to doozer config
var Config *logyardConfig

const DOOZER_PREFIX = "/proc/logyard/config/"

var doozerCfg *doozerconfig.DoozerConfig

// Init initializes logyard with the given config entry point in
// doozer.
func Init(conn *doozer.Conn, rev int64, monitor bool) {
	Config = new(logyardConfig)
	Config.Drains = make(map[string]string)
	Config.RetryLimits = make(map[string]string)
	Config.DrainFormats = make(map[string]string)
	Config.Ch = make(chan *doozerconfig.Change)
	Config.Rev = rev
	Config.Doozer = conn
	doozerCfg = doozerconfig.New(conn, rev, Config, DOOZER_PREFIX)
	err := doozerCfg.Load()
	if err != nil {
		log.Fatal(err)
	}

	if !monitor {
		return
	}

	// Monitor drain config changes in doozer
	go doozerCfg.Monitor(DOOZER_PREFIX+"drains/*", func(change *doozerconfig.Change, err error) {
		if err != nil {
			// don't bring down the entire logyard, because we want
			// the existing drains to continue to function despite an
			// error in monitoring config changes.
			log.Errorf("Unable to process drain config change in doozer: %s", err)
			return
		}
		log.Infof("Detected change in doozer drains config: %s", change.Key)
		// Config.Rev = TODO
		if change.FieldName == "Drains" {
			switch change.Type {
			case doozerconfig.DELETE, doozerconfig.SET:
				Config.Ch <- change
			}
		}
	})
}

// DeleteDrain deletes the drain from doozer tree
func (Config *logyardConfig) DeleteDrain(name string) error {
	err := Config.Doozer.Del(DOOZER_PREFIX+"drains/"+name, -1)
	if err != nil {
		return err
	}
	return nil
}

// AddDrain adds the drain to the doozer tree
func (Config *logyardConfig) AddDrain(name string, uri string) error {
	data, err := json.Marshal(uri)
	if err != nil {
		return err
	}
	_, err = Config.Doozer.Set(DOOZER_PREFIX+"drains/"+name, Config.Rev, data)
	if err != nil {
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/voxelbrain/pixelpixel/pixelutils"
	"time"
)

func main() {
	wall, _ := pixelutils.PixelPusher()
	pixel := pixelutils.NewPixel()
	bigPixel := pixelutils.DimensionChanger(pixel, 5*4, 18)
	textPixel := pixelutils.NewImageWriter(bigPixel, pixelutils.Green)

	colon := ":"
	for {
		pixelutils.Empty(bigPixel)
		if colon == ":" {
			colon = " "
		} else {
			colon = ":"
		}

		textPixel.Cls()
		fmt.Fprintf(textPixel, "%02d%s%02d", time.Now().Hour(), colon, time.Now().Minute())
		wall <- pixel
		time.Sleep(500 * time.Millisecond)
	}
}
<commit_msg>Use generator for clock example<commit_after>package main

import (
	"fmt"
	"github.com/voxelbrain/pixelpixel/pixelutils"
	"time"
)

func colonGenerator() <-chan string {
	c := make(chan string)
	go func() {
		for {
			c <- ":"
			c <- " "
		}
	}()
	return c
}

func main() {
	wall, _ := pixelutils.PixelPusher()
	pixel := pixelutils.NewPixel()
	bigPixel := pixelutils.DimensionChanger(pixel, 5*4, 18)
	textPixel := pixelutils.NewImageWriter(bigPixel, pixelutils.Green)

	colons := colonGenerator()
	for {
		pixelutils.Empty(bigPixel)
		textPixel.Cls()
		fmt.Fprintf(textPixel, "%02d%s%02d", time.Now().Hour(), <-colons, time.Now().Minute())
		wall <- pixel
		time.Sleep(500 * time.Millisecond)
	}
}
<|endoftext|>


<commit_before>package api

import (
	"fmt"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"sync"
	"time"

	"database/sql"

	"github.com/labstack/echo"
	"github.com/tikasan/eventory/define"
	"github.com/tikasan/eventory/model"
)

// TODO ネーミング変えるべきかも
type Inserter struct {
	DB *sql.DB
}

func (i *Inserter) EventFetch(c echo.Context) error {

	g, err := os.Getwd()
	if err != nil {
		log.Fatal(err)
	}

	checkLogPath := filepath.Join(g, "log", "check.log")
	_, err = os.Stat(checkLogPath)
	if err != nil {
		fmt.Fprint(os.Stderr, err)
		return c.JSON(http.StatusInternalServerError, "log/check.log not found")
	}

	checkLog, err := os.OpenFile(checkLogPath, os.O_APPEND|os.O_WRONLY, 0600)
	if err != nil {
		return c.JSON(http.StatusInternalServerError, "log/check.log cant open")
	}
	defer checkLog.Close()

	_, err = http.Head(define.ATDN_URL)
	if err != nil {
		writeLog(checkLog, "[err atdn cant access]")
		return c.JSON(http.StatusBadRequest, "atdn cant api access")
	}
	_, err = http.Head(define.CONNPASS_URL)
	if err != nil {
		writeLog(checkLog, "[err connpass cant access]")
		return c.JSON(http.StatusBadRequest, "connpass cant api access")
	}
	_, err = http.Head(define.DOORKEEPER_URL)
	if err != nil {
		writeLog(checkLog, "[err doorkeeper cant access]")
		return c.JSON(http.StatusBadRequest, "doorkeeper cant api access")
	}

	receiver := communication()

	for {
		receive, ok := <-receiver
		if !ok {
			break
		} else {
			err := model.Insert(i.DB, receive)
			if err != nil {
				writeLog(checkLog, "[err database insert]")
				return c.JSON(http.StatusInternalServerError, "Database Insert Error")
			}
		}

	}

	writeLog(checkLog, "[ok fetch event]")
	return c.JSON(http.StatusOK, "OK")
}

func communication() <-chan []model.Event {
	now := time.Now()
	atdn := make([]Request, define.SERACH_SCOPE)
	connpass := make([]Request, define.SERACH_SCOPE)
	doorKeeper := make([]Request, define.SERACH_SCOPE)
	allRequest := make([]Request, 0)

	for i := 0; i < define.SERACH_SCOPE; i++ {
		ym := now.AddDate(0, i, 0).Format("200601")
		atdn[i].Url = fmt.Sprintf("%s&ym=%s", define.ATDN_URL, ym)
		atdn[i].Api = define.ATDN

		connpass[i].Url = fmt.Sprintf("%s&ym=%s", define.CONNPASS_URL, ym)
		connpass[i].Api = define.CONNPASS

		doorKeeper[i].Url = fmt.Sprintf("%s?page=%d", define.DOORKEEPER_URL, i)
		doorKeeper[i].Api = define.DOORKEEPER
		doorKeeper[i].Token = ""
	}

	allRequest = append(allRequest, atdn...)
	allRequest = append(allRequest, connpass...)
	allRequest = append(allRequest, doorKeeper...)
	allEvents := make(chan []model.Event, len(allRequest))
	var wg sync.WaitGroup

	go func() {
		for _, r := range allRequest {
			wg.Add(1)
			go func(r Request) {
				cli := NewRequest(r.Url, r.Api, r.Token)
				events, err := cli.Get()
				if err != nil {
					fmt.Fprint(os.Stderr, err)
					wg.Done()
				}
				allEvents <- events
				wg.Done()
			}(r)
		}
		wg.Wait()
		close(allEvents)
	}()
	return allEvents
}

func (i *Inserter) GetEvent(c echo.Context) error {
	event, err := model.EventAllNew(i.DB)
	if err != nil {
		fmt.Fprint(os.Stderr, err)
	}
	c.Response().Header().Set("Content-Type", "application/json")
	return c.JSON(http.StatusOK, event)
}

func writeLog(checkLog *os.File, message string) {
	now := time.Now()
	logger := log.New(checkLog, message, log.LstdFlags)
	logger.Println(now)
	checkLog.Sync()
}
<commit_msg>update: errログの統一<commit_after>package api

import (
	"fmt"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"sync"
	"time"

	"database/sql"

	"github.com/labstack/echo"
	"github.com/tikasan/eventory/define"
	"github.com/tikasan/eventory/model"
)

// TODO ネーミング変えるべきかも
type Inserter struct {
	DB *sql.DB
}

func (i *Inserter) EventFetch(c echo.Context) error {

	g, err := os.Getwd()
	if err != nil {
		log.Fatal(err)
	}

	checkLogPath := filepath.Join(g, "log", "check.log")
	_, err = os.Stat(checkLogPath)
	if err != nil {
		fmt.Fprint(os.Stderr, err)
		return c.JSON(http.StatusInternalServerError, "[err][log/check.log not found]")
	}

	checkLog, err := os.OpenFile(checkLogPath, os.O_APPEND|os.O_WRONLY, 0600)
	if err != nil {
		return c.JSON(http.StatusInternalServerError, "[err][log/check.log cant open]")
	}
	defer checkLog.Close()

	_, err = http.Head(define.ATDN_URL)
	if err != nil {
		writeLog(checkLog, "[err][atdn cant access]")
		return c.JSON(http.StatusBadRequest, "[err][atdn cant access]")
	}
	_, err = http.Head(define.CONNPASS_URL)
	if err != nil {
		writeLog(checkLog, "[err][connpass cant access]")
		return c.JSON(http.StatusBadRequest, "[err][connpass cant access]")
	}
	_, err = http.Head(define.DOORKEEPER_URL)
	if err != nil {
		writeLog(checkLog, "[err][doorkeeper cant access]")
		return c.JSON(http.StatusBadRequest, "[err][doorkeeper cant access]")
	}

	receiver := communication()

	for {
		receive, ok := <-receiver
		if !ok {
			break
		} else {
			err := model.Insert(i.DB, receive)
			if err != nil {
				writeLog(checkLog, "[err][database insert]")
				return c.JSON(http.StatusInternalServerError, "[err][database insert]")
			}
		}

	}

	writeLog(checkLog, "[success][fetch event]")
	return c.JSON(http.StatusOK, "OK")
}

func communication() <-chan []model.Event {
	now := time.Now()
	atdn := make([]Request, define.SERACH_SCOPE)
	connpass := make([]Request, define.SERACH_SCOPE)
	doorKeeper := make([]Request, define.SERACH_SCOPE)
	allRequest := make([]Request, 0)

	for i := 0; i < define.SERACH_SCOPE; i++ {
		ym := now.AddDate(0, i, 0).Format("200601")
		atdn[i].Url = fmt.Sprintf("%s&ym=%s", define.ATDN_URL, ym)
		atdn[i].Api = define.ATDN

		connpass[i].Url = fmt.Sprintf("%s&ym=%s", define.CONNPASS_URL, ym)
		connpass[i].Api = define.CONNPASS

		doorKeeper[i].Url = fmt.Sprintf("%s?page=%d", define.DOORKEEPER_URL, i)
		doorKeeper[i].Api = define.DOORKEEPER
		doorKeeper[i].Token = ""
	}

	allRequest = append(allRequest, atdn...)
	allRequest = append(allRequest, connpass...)
	allRequest = append(allRequest, doorKeeper...)
	allEvents := make(chan []model.Event, len(allRequest))
	var wg sync.WaitGroup

	go func() {
		for _, r := range allRequest {
			wg.Add(1)
			go func(r Request) {
				cli := NewRequest(r.Url, r.Api, r.Token)
				events, err := cli.convertingToJson()
				if err != nil {
					fmt.Fprint(os.Stderr, err)
					wg.Done()
				}
				allEvents <- events
				wg.Done()
			}(r)
		}
		wg.Wait()
		close(allEvents)
	}()
	return allEvents
}

func (i *Inserter) GetEvent(c echo.Context) error {
	event, err := model.EventAllNew(i.DB)
	if err != nil {
		fmt.Fprint(os.Stderr, err)
	}
	c.Response().Header().Set("Content-Type", "application/json")
	return c.JSON(http.StatusOK, event)
}

func writeLog(checkLog *os.File, message string) {
	now := time.Now()
	logger := log.New(checkLog, message, log.LstdFlags)
	logger.Println(now)
	checkLog.Sync()
}
<|endoftext|>


<commit_before>package arn

import (
	"errors"
	"fmt"
	"reflect"
	"strings"

	"github.com/aerogo/aero"
	"github.com/aerogo/api"
	"github.com/aerogo/markdown"
	"github.com/animenotifier/arn/autocorrect"
)

// Force interface implementations
var (
	_ Postable          = (*Post)(nil)
	_ Likeable          = (*Post)(nil)
	_ LikeEventReceiver = (*Post)(nil)
	_ PostParent        = (*Post)(nil)
	_ fmt.Stringer      = (*Post)(nil)
	_ api.Newable       = (*Post)(nil)
	_ api.Editable      = (*Post)(nil)
	_ api.Actionable    = (*Post)(nil)
	_ api.Deletable     = (*Post)(nil)
)

// Actions
func init() {
	API.RegisterActions("Post", []*api.Action{
		// Like post
		LikeAction(),

		// Unlike post
		UnlikeAction(),
	})
}

// Authorize returns an error if the given API POST request is not authorized.
func (post *Post) Authorize(ctx *aero.Context, action string) error {
	if !ctx.HasSession() {
		return errors.New("Neither logged in nor in session")
	}

	if action == "edit" {
		user := GetUserFromContext(ctx)

		if post.CreatedBy != user.ID && user.Role != "admin" {
			return errors.New("Can't edit the posts of other users")
		}
	}

	return nil
}

// Create sets the data for a new post with data we received from the API request.
func (post *Post) Create(ctx *aero.Context) error {
	data, err := ctx.Request().Body().JSONObject()

	if err != nil {
		return err
	}

	user := GetUserFromContext(ctx)

	if user == nil {
		return errors.New("Not logged in")
	}

	post.ID = GenerateID("Post")
	post.Text, _ = data["text"].(string)
	post.CreatedBy = user.ID
	post.ParentID, _ = data["parentId"].(string)
	post.ParentType, _ = data["parentType"].(string)
	post.Likes = []string{}
	post.Created = DateTimeUTC()
	post.Edited = ""

	// Check parent type
	if !DB.HasType(post.ParentType) {
		return errors.New("Invalid parent type: " + post.ParentType)
	}

	// Post-process text
	post.Text = autocorrect.PostText(post.Text)

	if len(post.Text) < 5 {
		return errors.New("Text too short: Should be at least 5 characters")
	}

	// Tags
	tags, _ := data["tags"].([]interface{})
	post.Tags = make([]string, len(tags))

	for i := range post.Tags {
		post.Tags[i] = tags[i].(string)
	}

	// Thread
	parent := post.Parent()

	if parent == nil {
		return errors.New(post.ParentType + " does not exist")
	}

	// Is the parent locked?
	if IsLocked(parent) {
		return errors.New(post.ParentType + " is locked")
	}

	// Append to posts
	parent.AddPost(post.ID)

	// Save the parent thread
	parent.Save()

	// Send notification to the author of the parent post
	go func() {
		notifyUser := parent.Creator()
		message := ""

		if post.ParentType == "Post" {
			message = fmt.Sprintf("%s replied to your comment in \"%s\".", user.Nick, parent.(*Post).Parent().TitleByUser(notifyUser))
		} else {
			message = fmt.Sprintf("%s replied in the %s \"%s\".", user.Nick, strings.ToLower(post.ParentType), parent.TitleByUser(notifyUser))
		}

		notifyUser.SendNotification(&PushNotification{
			Title:   user.Nick + " replied",
			Message: message,
			Icon:    "https:" + user.AvatarLink("large"),
			Link:    post.Link(),
			Type:    NotificationTypeForumReply,
		})
	}()

	// Write log entry
	logEntry := NewEditLogEntry(user.ID, "create", "Post", post.ID, "", "", "")
	logEntry.Save()

	return nil
}

// Edit saves a log entry for the edit.
func (post *Post) Edit(ctx *aero.Context, key string, value reflect.Value, newValue reflect.Value) (bool, error) {
	user := GetUserFromContext(ctx)

	// Write log entry
	logEntry := NewEditLogEntry(user.ID, "edit", "Post", post.ID, key, fmt.Sprint(value.Interface()), fmt.Sprint(newValue.Interface()))
	logEntry.Save()

	return false, nil
}

// OnAppend saves a log entry.
func (post *Post) OnAppend(ctx *aero.Context, key string, index int, obj interface{}) {
	user := GetUserFromContext(ctx)
	logEntry := NewEditLogEntry(user.ID, "arrayAppend", "Post", post.ID, fmt.Sprintf("%s[%d]", key, index), "", fmt.Sprint(obj))
	logEntry.Save()
}

// OnRemove saves a log entry.
func (post *Post) OnRemove(ctx *aero.Context, key string, index int, obj interface{}) {
	user := GetUserFromContext(ctx)
	logEntry := NewEditLogEntry(user.ID, "arrayRemove", "Post", post.ID, fmt.Sprintf("%s[%d]", key, index), fmt.Sprint(obj), "")
	logEntry.Save()
}

// DeleteInContext deletes the post in the given context.
func (post *Post) DeleteInContext(ctx *aero.Context) error {
	user := GetUserFromContext(ctx)

	// Write log entry
	logEntry := NewEditLogEntry(user.ID, "delete", "Post", post.ID, "", fmt.Sprint(post), "")
	logEntry.Save()

	return post.Delete()
}

// Delete deletes the post from the database.
func (post *Post) Delete() error {
	// Remove child posts first
	for _, post := range post.Posts() {
		post.Delete()
	}

	parent := post.Parent()

	if parent == nil {
		return fmt.Errorf("Invalid %s parent ID: %s", post.ParentType, post.ParentID)
	}

	// Remove the reference of the post in the thread that contains it
	if !parent.RemovePost(post.ID) {
		return fmt.Errorf("This post does not exist in the %s", strings.ToLower(post.ParentType))
	}

	parent.Save()
	DB.Delete("Post", post.ID)
	return nil
}

// AfterEdit updates the date it has been edited.
func (post *Post) AfterEdit(ctx *aero.Context) error {
	post.Edited = DateTimeUTC()
	post.html = markdown.Render(post.Text)
	return nil
}

// Save saves the post object in the database.
func (post *Post) Save() {
	DB.Set("Post", post.ID, post)
}
<commit_msg>Don't notify the author<commit_after>package arn

import (
	"errors"
	"fmt"
	"reflect"
	"strings"

	"github.com/aerogo/aero"
	"github.com/aerogo/api"
	"github.com/aerogo/markdown"
	"github.com/animenotifier/arn/autocorrect"
)

// Force interface implementations
var (
	_ Postable          = (*Post)(nil)
	_ Likeable          = (*Post)(nil)
	_ LikeEventReceiver = (*Post)(nil)
	_ PostParent        = (*Post)(nil)
	_ fmt.Stringer      = (*Post)(nil)
	_ api.Newable       = (*Post)(nil)
	_ api.Editable      = (*Post)(nil)
	_ api.Actionable    = (*Post)(nil)
	_ api.Deletable     = (*Post)(nil)
)

// Actions
func init() {
	API.RegisterActions("Post", []*api.Action{
		// Like post
		LikeAction(),

		// Unlike post
		UnlikeAction(),
	})
}

// Authorize returns an error if the given API POST request is not authorized.
func (post *Post) Authorize(ctx *aero.Context, action string) error {
	if !ctx.HasSession() {
		return errors.New("Neither logged in nor in session")
	}

	if action == "edit" {
		user := GetUserFromContext(ctx)

		if post.CreatedBy != user.ID && user.Role != "admin" {
			return errors.New("Can't edit the posts of other users")
		}
	}

	return nil
}

// Create sets the data for a new post with data we received from the API request.
func (post *Post) Create(ctx *aero.Context) error {
	data, err := ctx.Request().Body().JSONObject()

	if err != nil {
		return err
	}

	user := GetUserFromContext(ctx)

	if user == nil {
		return errors.New("Not logged in")
	}

	post.ID = GenerateID("Post")
	post.Text, _ = data["text"].(string)
	post.CreatedBy = user.ID
	post.ParentID, _ = data["parentId"].(string)
	post.ParentType, _ = data["parentType"].(string)
	post.Likes = []string{}
	post.Created = DateTimeUTC()
	post.Edited = ""

	// Check parent type
	if !DB.HasType(post.ParentType) {
		return errors.New("Invalid parent type: " + post.ParentType)
	}

	// Post-process text
	post.Text = autocorrect.PostText(post.Text)

	if len(post.Text) < 5 {
		return errors.New("Text too short: Should be at least 5 characters")
	}

	// Tags
	tags, _ := data["tags"].([]interface{})
	post.Tags = make([]string, len(tags))

	for i := range post.Tags {
		post.Tags[i] = tags[i].(string)
	}

	// Thread
	parent := post.Parent()

	if parent == nil {
		return errors.New(post.ParentType + " does not exist")
	}

	// Is the parent locked?
	if IsLocked(parent) {
		return errors.New(post.ParentType + " is locked")
	}

	// Append to posts
	parent.AddPost(post.ID)

	// Save the parent thread
	parent.Save()

	// Send notification to the author of the parent post
	go func() {
		notifyUser := parent.Creator()

		// Don't notify the author himself
		if notifyUser.ID == post.CreatedBy {
			return
		}

		message := ""

		if post.ParentType == "Post" {
			message = fmt.Sprintf("%s replied to your comment in \"%s\".", user.Nick, parent.(*Post).Parent().TitleByUser(notifyUser))
		} else {
			message = fmt.Sprintf("%s replied in the %s \"%s\".", user.Nick, strings.ToLower(post.ParentType), parent.TitleByUser(notifyUser))
		}

		notifyUser.SendNotification(&PushNotification{
			Title:   user.Nick + " replied",
			Message: message,
			Icon:    "https:" + user.AvatarLink("large"),
			Link:    post.Link(),
			Type:    NotificationTypeForumReply,
		})
	}()

	// Write log entry
	logEntry := NewEditLogEntry(user.ID, "create", "Post", post.ID, "", "", "")
	logEntry.Save()

	return nil
}

// Edit saves a log entry for the edit.
func (post *Post) Edit(ctx *aero.Context, key string, value reflect.Value, newValue reflect.Value) (bool, error) {
	user := GetUserFromContext(ctx)

	// Write log entry
	logEntry := NewEditLogEntry(user.ID, "edit", "Post", post.ID, key, fmt.Sprint(value.Interface()), fmt.Sprint(newValue.Interface()))
	logEntry.Save()

	return false, nil
}

// OnAppend saves a log entry.
func (post *Post) OnAppend(ctx *aero.Context, key string, index int, obj interface{}) {
	user := GetUserFromContext(ctx)
	logEntry := NewEditLogEntry(user.ID, "arrayAppend", "Post", post.ID, fmt.Sprintf("%s[%d]", key, index), "", fmt.Sprint(obj))
	logEntry.Save()
}

// OnRemove saves a log entry.
func (post *Post) OnRemove(ctx *aero.Context, key string, index int, obj interface{}) {
	user := GetUserFromContext(ctx)
	logEntry := NewEditLogEntry(user.ID, "arrayRemove", "Post", post.ID, fmt.Sprintf("%s[%d]", key, index), fmt.Sprint(obj), "")
	logEntry.Save()
}

// DeleteInContext deletes the post in the given context.
func (post *Post) DeleteInContext(ctx *aero.Context) error {
	user := GetUserFromContext(ctx)

	// Write log entry
	logEntry := NewEditLogEntry(user.ID, "delete", "Post", post.ID, "", fmt.Sprint(post), "")
	logEntry.Save()

	return post.Delete()
}

// Delete deletes the post from the database.
func (post *Post) Delete() error {
	// Remove child posts first
	for _, post := range post.Posts() {
		post.Delete()
	}

	parent := post.Parent()

	if parent == nil {
		return fmt.Errorf("Invalid %s parent ID: %s", post.ParentType, post.ParentID)
	}

	// Remove the reference of the post in the thread that contains it
	if !parent.RemovePost(post.ID) {
		return fmt.Errorf("This post does not exist in the %s", strings.ToLower(post.ParentType))
	}

	parent.Save()
	DB.Delete("Post", post.ID)
	return nil
}

// AfterEdit updates the date it has been edited.
func (post *Post) AfterEdit(ctx *aero.Context) error {
	post.Edited = DateTimeUTC()
	post.html = markdown.Render(post.Text)
	return nil
}

// Save saves the post object in the database.
func (post *Post) Save() {
	DB.Set("Post", post.ID, post)
}
<|endoftext|>