<commit_before>package wally

import (
	"fmt"
	"io/ioutil"
	"os"
	"strings"
	"testing"

	rdb "github.com/dancannon/gorethink"
	"github.com/stretchr/testify/assert"
)

var (
	session *rdb.Session
)

func init() {
	Database = "testing"
	DocumentTable = "documents"
	IndexTable = "indexes"

	var err error
	session, err = rdb.Connect(rdb.ConnectOpts{
		Address:  os.Getenv("RETHINKDB_URL"),
		Database: "test",
	})

	if err != nil {
		fmt.Errorf(err.Error())
	}

	// Reset database
	rdb.DbDrop(Database).Exec(session)
	rdb.DbCreate(Database).Exec(session)
}

func DbBootstrap() {
	// Drop tables
	rdb.Db(Database).TableDrop(DocumentTable).Run(session)
	rdb.Db(Database).TableDrop(IndexTable).Run(session)

	// Create tables
	rdb.Db(Database).TableCreate(DocumentTable).Run(session)
	rdb.Db(Database).TableCreate(IndexTable).Run(session)
}

func TestIndexer_Stopper(t *testing.T) {
	tests := []struct {
		Input  string
		Output string
	}{
		{
			"computer",
			"computer",
		},

		{
			"the",
			"",
		},

		{
			"technology",
			"technology",
		},

		{
			"wasn't",
			"",
		},
	}

	for _, test := range tests {
		assert.Equal(t, test.Output, Stopper(test.Input))
	}
}

func TestIndexer_SplitTextIntoWords(t *testing.T) {
	tests := []struct {
		Input  interface{}
		Output []string
	}{
		{
			"I am a block of text",
			[]string{"I", "am", "a", "block", "of", "text"},
		},

		{
			"        superfluous    whitespace ",
			[]string{"superfluous", "whitespace"},
		},

		{
			"              ",
			[]string{},
		},

		{
			[]byte("fancy a byte?"),
			[]string{"fancy", "a", "byte?"},
		},

		{
			32,
			[]string{"32"},
		},

		{
			true,
			[]string{},
		},
	}

	for _, test := range tests {
		assert.Equal(t, test.Output, SplitTextIntoWords(test.Input))
	}
}

func TestIndexer_Indexer(t *testing.T) {
	docId := "12345-67890-ABCDE"

	tests := []struct {
		Input  interface{}
		Output []Index
	}{
		{
			"I am a block of text and I am going to be parsed",
			[]Index{
				Index{Word: "block", DocumentId: docId, Count: 0},
				Index{Word: "text", DocumentId: docId, Count: 0},
				Index{Word: "going", DocumentId: docId, Count: 0},
				Index{Word: "parsed", DocumentId: docId, Count: 0},
			},
		},

		{
			[]byte("I am another block of text but now I am in bytes"),
			[]Index{
				Index{Word: "another", DocumentId: docId, Count: 0},
				Index{Word: "block", DocumentId: docId, Count: 0},
				Index{Word: "text", DocumentId: docId, Count: 0},
				Index{Word: "now", DocumentId: docId, Count: 0},
				Index{Word: "bytes", DocumentId: docId, Count: 0},
			},
		},
	}

	for _, test := range tests {
		assert.Equal(t, test.Output, Indexer(test.Input, docId))
	}
}

func TestIndexer_IndexString(t *testing.T) {
	indexId := "world"

	index := Index{Id: indexId}

	assert.Equal(t, "Index#world", index.String())
}

func TestIndexer_IndexPut(t *testing.T) {
	DbBootstrap()

	index := Index{Word: "hello", Count: 5, DocumentId: "12345-67890-ABCDE"}

	err := index.Put(session)
	assert.Nil(t, err)

	res, err := rdb.Db(Database).Table(IndexTable).Get(index.Id).Run(session)
	assert.Nil(t, err)

	var i Index
	err = res.One(&i)
	assert.Nil(t, err)

	assert.NotEqual(t, i.Id, "")
	assert.Equal(t, i.Word, "hello")
	assert.Equal(t, i.Count, 5)
	assert.Equal(t, i.DocumentId, "12345-67890-ABCDE")
}

func TestIndexer_DocumentString(t *testing.T) {
	docId := "12345-67890-ABCDE"

	doc := Document{Id: docId}

	assert.Equal(t, "Document#12345-67890-ABCDE", doc.String())
}

func TestIndexer_DocumentPut(t *testing.T) {
	DbBootstrap()

	doc := Document{
		Source:  "www.google.com",
		Content: "Lorem ipsum dolor sit amet.",
	}

	err := doc.Put(session)
	assert.Nil(t, err)

	res, err := rdb.Db(Database).Table(DocumentTable).Run(session)
	assert.Nil(t, err)

	var d Document
	err = res.One(&d)
	assert.Nil(t, err)

	assert.NotEqual(t, d.Id, "")
	assert.Equal(t, d.Source, "www.google.com")
	assert.Equal(t, d.Content, "Lorem ipsum dolor sit amet.")
}

func BenchmarkSplitTextIntoWords(b *testing.B) {
	file, err := ioutil.ReadFile("test_data/test.txt") // 30654 words
	if err != nil {
		b.Error("Could not load test data")
	}

	for n := 0; n < b.N; n++ {
		SplitTextIntoWords(file)
	}
}

func BenchmarkSplitTextIntoWords_two(b *testing.B) {
	file, err := ioutil.ReadFile("test_data/test_2.txt") // 1891 words
	if err != nil {
		b.Error("Could not load test data")
	}

	for n := 0; n < b.N; n++ {
		SplitTextIntoWords(file)
	}
}

func BenchmarkStopper(b *testing.B) {
	file, err := ioutil.ReadFile("test_data/test.txt") // 30654 words
	if err != nil {
		b.Error("Could not load test data")
	}

	data := strings.Fields(string(file))

	for n := 0; n < b.N; n++ {
		for _, word := range data {
			Stopper(word)
		}
	}
}

func BenchmarkStopper_two(b *testing.B) {
	file, err := ioutil.ReadFile("test_data/test_2.txt") // 1891 words
	if err != nil {
		b.Error("Could not load test data")
	}

	data := strings.Fields(string(file))

	for n := 0; n < b.N; n++ {
		for _, word := range data {
			Stopper(word)
		}
	}
}

func BenchmarkIndexer(b *testing.B) {
	docId := "12345-67890-ABCDE"
	file, err := ioutil.ReadFile("test_data/test.txt") // 30654 words
	if err != nil {
		b.Error("Could not load test data")
	}

	for n := 0; n < b.N; n++ {
		Indexer(file, docId)
	}
}

func BenchmarkIndexer_two(b *testing.B) {
	docId := "12345-67890-ABCDE"
	file, err := ioutil.ReadFile("test_data/test_2.txt") // 30654 words
	if err != nil {
		b.Error("Could not load test data")
	}

	for n := 0; n < b.N; n++ {
		Indexer(file, docId)
	}
}
<commit_msg>fix #8 fixes broken test<commit_after>package wally

import (
	"fmt"
	"io/ioutil"
	"os"
	"strings"
	"testing"

	rdb "github.com/dancannon/gorethink"
	"github.com/stretchr/testify/assert"
)

var (
	session *rdb.Session
)

func init() {
	Database = "testing"
	DocumentTable = "documents"
	IndexTable = "indexes"

	var err error
	session, err = rdb.Connect(rdb.ConnectOpts{
		Address:  os.Getenv("RETHINKDB_URL"),
		Database: "test",
	})

	if err != nil {
		fmt.Errorf(err.Error())
	}

	// Reset database
	rdb.DbDrop(Database).Exec(session)
	rdb.DbCreate(Database).Exec(session)
}

func DbBootstrap() {
	// Drop tables
	rdb.Db(Database).TableDrop(DocumentTable).Run(session)
	rdb.Db(Database).TableDrop(IndexTable).Run(session)

	// Create tables
	rdb.Db(Database).TableCreate(DocumentTable).Run(session)
	rdb.Db(Database).TableCreate(IndexTable).Run(session)
}

func TestIndexer_Stopper(t *testing.T) {
	tests := []struct {
		Input  string
		Output string
	}{
		{
			"computer",
			"computer",
		},

		{
			"the",
			"",
		},

		{
			"technology",
			"technology",
		},

		{
			"wasn't",
			"",
		},
	}

	for _, test := range tests {
		assert.Equal(t, test.Output, Stopper(test.Input))
	}
}

func TestIndexer_SplitTextIntoWords(t *testing.T) {
	tests := []struct {
		Input  interface{}
		Output []string
	}{
		{
			"I am a block of text",
			[]string{"I", "am", "a", "block", "of", "text"},
		},

		{
			"        superfluous    whitespace ",
			[]string{"superfluous", "whitespace"},
		},

		{
			"              ",
			[]string{},
		},

		{
			[]byte("fancy a byte?"),
			[]string{"fancy", "a", "byte?"},
		},

		{
			32,
			[]string{"32"},
		},

		{
			true,
			[]string{},
		},
	}

	for _, test := range tests {
		assert.Equal(t, test.Output, SplitTextIntoWords(test.Input))
	}
}

func TestIndexer_Indexer(t *testing.T) {
	docId := "12345-67890-ABCDE"

	tests := []struct {
		Input  interface{}
		Output []Index
	}{
		{
			"I am a block of text and I am going to be parsed",
			[]Index{
				Index{Word: "block", DocumentId: docId, Count: 0},
				Index{Word: "text", DocumentId: docId, Count: 0},
				Index{Word: "going", DocumentId: docId, Count: 0},
				Index{Word: "parsed", DocumentId: docId, Count: 0},
			},
		},

		{
			[]byte("I am another block of text but now I am in bytes"),
			[]Index{
				Index{Word: "another", DocumentId: docId, Count: 0},
				Index{Word: "block", DocumentId: docId, Count: 0},
				Index{Word: "text", DocumentId: docId, Count: 0},
				Index{Word: "now", DocumentId: docId, Count: 0},
				Index{Word: "bytes", DocumentId: docId, Count: 0},
			},
		},
	}

	for x, test := range tests {
		i := Indexer(test.Input, docId)
		assert.NotEqual(t, "", i[x].Id)
	}
}

func TestIndexer_IndexString(t *testing.T) {
	indexId := "world"

	index := Index{Id: indexId}

	assert.Equal(t, "Index#world", index.String())
}

func TestIndexer_IndexPut(t *testing.T) {
	DbBootstrap()

	index := Index{Word: "hello", Count: 5, DocumentId: "12345-67890-ABCDE"}

	err := index.Put(session)
	assert.Nil(t, err)

	res, err := rdb.Db(Database).Table(IndexTable).Get(index.Id).Run(session)
	assert.Nil(t, err)

	var i Index
	err = res.One(&i)
	assert.Nil(t, err)

	assert.NotEqual(t, i.Id, "")
	assert.Equal(t, i.Word, "hello")
	assert.Equal(t, i.Count, 5)
	assert.Equal(t, i.DocumentId, "12345-67890-ABCDE")
}

func TestIndexer_DocumentString(t *testing.T) {
	docId := "12345-67890-ABCDE"

	doc := Document{Id: docId}

	assert.Equal(t, "Document#12345-67890-ABCDE", doc.String())
}

func TestIndexer_DocumentPut(t *testing.T) {
	DbBootstrap()

	doc := Document{
		Source:  "www.google.com",
		Content: "Lorem ipsum dolor sit amet.",
	}

	err := doc.Put(session)
	assert.Nil(t, err)

	res, err := rdb.Db(Database).Table(DocumentTable).Run(session)
	assert.Nil(t, err)

	var d Document
	err = res.One(&d)
	assert.Nil(t, err)

	assert.NotEqual(t, d.Id, "")
	assert.Equal(t, d.Source, "www.google.com")
	assert.Equal(t, d.Content, "Lorem ipsum dolor sit amet.")
}

func BenchmarkSplitTextIntoWords(b *testing.B) {
	file, err := ioutil.ReadFile("test_data/test.txt") // 30654 words
	if err != nil {
		b.Error("Could not load test data")
	}

	for n := 0; n < b.N; n++ {
		SplitTextIntoWords(file)
	}
}

func BenchmarkSplitTextIntoWords_two(b *testing.B) {
	file, err := ioutil.ReadFile("test_data/test_2.txt") // 1891 words
	if err != nil {
		b.Error("Could not load test data")
	}

	for n := 0; n < b.N; n++ {
		SplitTextIntoWords(file)
	}
}

func BenchmarkStopper(b *testing.B) {
	file, err := ioutil.ReadFile("test_data/test.txt") // 30654 words
	if err != nil {
		b.Error("Could not load test data")
	}

	data := strings.Fields(string(file))

	for n := 0; n < b.N; n++ {
		for _, word := range data {
			Stopper(word)
		}
	}
}

func BenchmarkStopper_two(b *testing.B) {
	file, err := ioutil.ReadFile("test_data/test_2.txt") // 1891 words
	if err != nil {
		b.Error("Could not load test data")
	}

	data := strings.Fields(string(file))

	for n := 0; n < b.N; n++ {
		for _, word := range data {
			Stopper(word)
		}
	}
}

func BenchmarkIndexer(b *testing.B) {
	docId := "12345-67890-ABCDE"
	file, err := ioutil.ReadFile("test_data/test.txt") // 30654 words
	if err != nil {
		b.Error("Could not load test data")
	}

	for n := 0; n < b.N; n++ {
		Indexer(file, docId)
	}
}

func BenchmarkIndexer_two(b *testing.B) {
	docId := "12345-67890-ABCDE"
	file, err := ioutil.ReadFile("test_data/test_2.txt") // 30654 words
	if err != nil {
		b.Error("Could not load test data")
	}

	for n := 0; n < b.N; n++ {
		Indexer(file, docId)
	}
}
<|endoftext|>


<commit_before>// Package strings generates the String() method for enumeration types.
package strings

import (
	"bytes"
	"code.google.com/p/go.tools/go/types"
	"fmt"
	"gnd.la/gen/genutil"
	"gnd.la/log"
	"path/filepath"
	"regexp"
	"strings"
)

type Transform int

const (
	ToLower Transform = iota + 1
	ToUpper
)

type TypeOptions struct {
	Transform  Transform
	SliceBegin int
	SliceEnd   int
}

type Options struct {
	Include     *regexp.Regexp
	Exclude     *regexp.Regexp
	TypeOptions map[string]*TypeOptions
}

func Gen(pkgName string, opts *Options) error {
	pkg, err := genutil.NewPackage(pkgName)
	if err != nil {
		return err
	}
	var buf bytes.Buffer
	buf.WriteString(fmt.Sprintf("package %s\n\n", pkg.Name()))
	buf.WriteString(genutil.AutogenString())
	buf.WriteString("import \"fmt\"\n\nvar _ = fmt.Sprintf\n")
	var include *regexp.Regexp
	var exclude *regexp.Regexp
	if opts != nil {
		include = opts.Include
		exclude = opts.Exclude
	}
	var methods bytes.Buffer
	scope := pkg.Scope()
	for _, v := range pkg.Types(include, exclude) {
		methods.Reset()
		if err := genString(v, scope, opts, &methods); err != nil {
			log.Warningf("Skipping %v: %s", v, err)
		}
		buf.WriteString(methods.String())
	}
	out := filepath.Join(pkg.Dir(), "gen_strings.go")
	log.Debugf("Writing autogenerated String() methods to %s", out)
	return genutil.WriteAutogen(out, buf.Bytes())
}

func genString(named *types.Named, scope *types.Scope, opts *Options, buf *bytes.Buffer) error {
	var values []string
	for _, v := range scope.Names() {
		obj := scope.Lookup(v)
		if _, ok := obj.(*types.Const); ok {
			if typ, ok := obj.Type().(*types.Named); ok && typ == named {
				values = append(values, obj.Name())
			}
		}
	}
	names := make([]string, len(values))
	copy(names, values)
	name := named.Obj().Name()
	if opts != nil {
		if typOptions := opts.TypeOptions[name]; typOptions != nil {
			sl := sliceFunc(typOptions.SliceBegin, typOptions.SliceEnd)
			for ii, v := range values {
				values[ii] = sl(v)
			}
			switch typOptions.Transform {
			case ToLower:
				for ii, v := range values {
					values[ii] = strings.ToLower(v)
				}
			case ToUpper:
				for ii, v := range values {
					values[ii] = strings.ToUpper(v)
				}
			}
		}
	}
	if len(values) > 0 {
		varname := strings.ToLower(name[:1])
		buf.WriteString(fmt.Sprintf("func (%s %s) String() string {\n", varname, name))
		buf.WriteString(fmt.Sprintf("switch %s {\n", varname))
		for ii, v := range values {
			buf.WriteString(fmt.Sprintf("case %s:\n", names[ii]))
			buf.WriteString(fmt.Sprintf("return %q\n", v))
		}
		buf.WriteString("}\n")
		fmt.Fprintf(buf, "return fmt.Sprintf(\"unknown %s %%v\", %s)", strings.ToLower(name), varname)
		buf.WriteString("}\n")
	}
	return nil
}

func sliceFunc(begin, end int) func(string) string {
	if end > 0 {
		return func(s string) string {
			return s[begin:end]
		}
	}
	return func(s string) string {
		return s[begin : len(s)+end]
	}
}
<commit_msg>Avoid an infinite loop on unknown values<commit_after>// Package strings generates the String() method for enumeration types.
package strings

import (
	"bytes"
	"code.google.com/p/go.tools/go/types"
	"fmt"
	"gnd.la/gen/genutil"
	"gnd.la/log"
	"path/filepath"
	"regexp"
	"strings"
)

type Transform int

const (
	ToLower Transform = iota + 1
	ToUpper
)

type TypeOptions struct {
	Transform  Transform
	SliceBegin int
	SliceEnd   int
}

type Options struct {
	Include     *regexp.Regexp
	Exclude     *regexp.Regexp
	TypeOptions map[string]*TypeOptions
}

func Gen(pkgName string, opts *Options) error {
	pkg, err := genutil.NewPackage(pkgName)
	if err != nil {
		return err
	}
	var buf bytes.Buffer
	buf.WriteString(fmt.Sprintf("package %s\n\n", pkg.Name()))
	buf.WriteString(genutil.AutogenString())
	buf.WriteString("import \"fmt\"\n\nvar _ = fmt.Sprintf\n")
	var include *regexp.Regexp
	var exclude *regexp.Regexp
	if opts != nil {
		include = opts.Include
		exclude = opts.Exclude
	}
	var methods bytes.Buffer
	scope := pkg.Scope()
	for _, v := range pkg.Types(include, exclude) {
		methods.Reset()
		if err := genString(v, scope, opts, &methods); err != nil {
			log.Warningf("Skipping %v: %s", v, err)
		}
		buf.WriteString(methods.String())
	}
	out := filepath.Join(pkg.Dir(), "gen_strings.go")
	log.Debugf("Writing autogenerated String() methods to %s", out)
	return genutil.WriteAutogen(out, buf.Bytes())
}

func genString(named *types.Named, scope *types.Scope, opts *Options, buf *bytes.Buffer) error {
	var values []string
	for _, v := range scope.Names() {
		obj := scope.Lookup(v)
		if _, ok := obj.(*types.Const); ok {
			if typ, ok := obj.Type().(*types.Named); ok && typ == named {
				values = append(values, obj.Name())
			}
		}
	}
	names := make([]string, len(values))
	copy(names, values)
	name := named.Obj().Name()
	if opts != nil {
		if typOptions := opts.TypeOptions[name]; typOptions != nil {
			sl := sliceFunc(typOptions.SliceBegin, typOptions.SliceEnd)
			for ii, v := range values {
				values[ii] = sl(v)
			}
			switch typOptions.Transform {
			case ToLower:
				for ii, v := range values {
					values[ii] = strings.ToLower(v)
				}
			case ToUpper:
				for ii, v := range values {
					values[ii] = strings.ToUpper(v)
				}
			}
		}
	}
	if len(values) > 0 {
		varname := strings.ToLower(name[:1])
		buf.WriteString(fmt.Sprintf("func (%s %s) String() string {\n", varname, name))
		buf.WriteString(fmt.Sprintf("switch %s {\n", varname))
		for ii, v := range values {
			buf.WriteString(fmt.Sprintf("case %s:\n", names[ii]))
			buf.WriteString(fmt.Sprintf("return %q\n", v))
		}
		buf.WriteString("}\n")
		fmt.Fprintf(buf, "return fmt.Sprintf(\"unknown %s %%d\", %s)", strings.ToLower(name), varname)
		buf.WriteString("}\n")
	}
	return nil
}

func sliceFunc(begin, end int) func(string) string {
	if end > 0 {
		return func(s string) string {
			return s[begin:end]
		}
	}
	return func(s string) string {
		return s[begin : len(s)+end]
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"log"
	"os"
	"time"

	"github.com/oinume/lekcije/server/bootstrap"
	"github.com/oinume/lekcije/server/config"
	"github.com/oinume/lekcije/server/errors"
	"github.com/oinume/lekcije/server/fetcher"
	"github.com/oinume/lekcije/server/logger"
	"github.com/oinume/lekcije/server/model"
	"github.com/oinume/lekcije/server/notifier"
	"github.com/uber-go/zap"
	"github.com/pkg/profile"
)

var (
	dryRun      = flag.Bool("dry-run", false, "Don't update database with fetched lessons")
	concurrency = flag.Int("concurrency", 1, "concurrency of fetcher")
	logLevel    = flag.String("log-level", "info", "Log level")
	profileMode = flag.String("profile-mode", "", "block or cpu or mem")
)

func main() {
	flag.Parse()
	if err := run(); err != nil {
		log.Fatalf("err = %v", err) // TODO: Error handling
	}
	os.Exit(0)
}

func run() error {
	switch *profileMode {
	case "block":
		defer profile.Start(profile.ProfilePath("."), profile.BlockProfile).Stop()
	case "cpu":
		defer profile.Start(profile.ProfilePath("."), profile.CPUProfile).Stop()
	case "mem":
		defer profile.Start(profile.ProfilePath("."), profile.MemProfile).Stop()
	}

	bootstrap.CheckCLIEnvVars()
	startedAt := time.Now().UTC()
	if *logLevel != "" {
		logger.App.SetLevel(logger.NewLevel(*logLevel))
	}
	logger.App.Info("notifier started")
	defer func() {
		elapsed := time.Now().UTC().Sub(startedAt) / time.Millisecond
		logger.App.Info("notifier finished", zap.Int("elapsed", int(elapsed)))
	}()

	db, err := model.OpenDB(bootstrap.CLIEnvVars.DBURL, 1, !config.IsProductionEnv())
	if err != nil {
		return err
	}
	defer db.Close()

	// TODO: Define method in UserService
	var users []*model.User
	userSQL := `SELECT * FROM user WHERE email_verified = 1`
	result := db.Raw(userSQL).Scan(&users)
	if result.Error != nil && !result.RecordNotFound() {
		return errors.InternalWrapf(result.Error, "Failed to find Users")
	}

	mCountries, err := model.NewMCountryService(db).LoadAll()
	if err != nil {
		return errors.InternalWrapf(err, "Failed to load all MCountries")
	}
	fetcher := fetcher.NewTeacherLessonFetcher(nil, *concurrency, mCountries, logger.App)
	notifier := notifier.NewNotifier(db, fetcher, *dryRun)
	defer notifier.Close()
	for _, user := range users {
		if err := notifier.SendNotification(user); err != nil {
			return err
		}
	}

	return nil
}
<commit_msg>Add trace profile<commit_after>package main

import (
	"flag"
	"log"
	"os"
	"time"

	"github.com/oinume/lekcije/server/bootstrap"
	"github.com/oinume/lekcije/server/config"
	"github.com/oinume/lekcije/server/errors"
	"github.com/oinume/lekcije/server/fetcher"
	"github.com/oinume/lekcije/server/logger"
	"github.com/oinume/lekcije/server/model"
	"github.com/oinume/lekcije/server/notifier"
	"github.com/uber-go/zap"
	"github.com/pkg/profile"
)

var (
	dryRun      = flag.Bool("dry-run", false, "Don't update database with fetched lessons")
	concurrency = flag.Int("concurrency", 1, "concurrency of fetcher")
	logLevel    = flag.String("log-level", "info", "Log level")
	profileMode = flag.String("profile-mode", "", "block|cpu|mem|trace")
)

func main() {
	flag.Parse()
	if err := run(); err != nil {
		log.Fatalf("err = %v", err) // TODO: Error handling
	}
	os.Exit(0)
}

func run() error {
	switch *profileMode {
	case "block":
		defer profile.Start(profile.ProfilePath("."), profile.BlockProfile).Stop()
	case "cpu":
		defer profile.Start(profile.ProfilePath("."), profile.CPUProfile).Stop()
	case "mem":
		defer profile.Start(profile.ProfilePath("."), profile.MemProfile).Stop()
	case "trace":
		defer profile.Start(profile.ProfilePath("."), profile.TraceProfile).Stop()
	}

	bootstrap.CheckCLIEnvVars()
	startedAt := time.Now().UTC()
	if *logLevel != "" {
		logger.App.SetLevel(logger.NewLevel(*logLevel))
	}
	logger.App.Info("notifier started")
	defer func() {
		elapsed := time.Now().UTC().Sub(startedAt) / time.Millisecond
		logger.App.Info("notifier finished", zap.Int("elapsed", int(elapsed)))
	}()

	db, err := model.OpenDB(bootstrap.CLIEnvVars.DBURL, 1, !config.IsProductionEnv())
	if err != nil {
		return err
	}
	defer db.Close()

	// TODO: Define method in UserService
	var users []*model.User
	userSQL := `SELECT * FROM user WHERE email_verified = 1`
	result := db.Raw(userSQL).Scan(&users)
	if result.Error != nil && !result.RecordNotFound() {
		return errors.InternalWrapf(result.Error, "Failed to find Users")
	}

	mCountries, err := model.NewMCountryService(db).LoadAll()
	if err != nil {
		return errors.InternalWrapf(err, "Failed to load all MCountries")
	}
	fetcher := fetcher.NewTeacherLessonFetcher(nil, *concurrency, mCountries, logger.App)
	notifier := notifier.NewNotifier(db, fetcher, *dryRun)
	defer notifier.Close()
	for _, user := range users {
		if err := notifier.SendNotification(user); err != nil {
			return err
		}
	}

	return nil
}
<|endoftext|>


<commit_before>// Package bot is a Hubot style bot that sits a microservice environment
package bot

import (
	"context"
	"fmt"
	"os"
	"regexp"
	"sort"
	"strings"
	"sync"
	"time"

	"github.com/micro/cli"
	"github.com/micro/go-micro"

	"github.com/micro/go-micro/agent/command"
	"github.com/micro/go-micro/agent/input"
	"github.com/micro/go-micro/util/log"
	botc "github.com/micro/micro/internal/command/bot"

	proto "github.com/micro/go-micro/agent/proto"

	// inputs
	_ "github.com/micro/go-micro/agent/input/discord"
	_ "github.com/micro/go-micro/agent/input/slack"
	_ "github.com/micro/go-micro/agent/input/telegram"
)

type bot struct {
	exit    chan bool
	ctx     *cli.Context
	service micro.Service

	sync.RWMutex
	inputs   map[string]input.Input
	commands map[string]command.Command
	services map[string]string
}

var (
	// Default server name
	Name = "go.micro.bot"
	// Namespace for commands
	Namespace = "go.micro.bot"
	// map pattern:command
	commands = map[string]func(*cli.Context) command.Command{
		"^echo ":                             botc.Echo,
		"^time$":                             botc.Time,
		"^hello$":                            botc.Hello,
		"^ping$":                             botc.Ping,
		"^list ":                             botc.List,
		"^get ":                              botc.Get,
		"^health ":                           botc.Health,
		"^call ":                             botc.Call,
		"^register ":                         botc.Register,
		"^deregister ":                       botc.Deregister,
		"^(the )?three laws( of robotics)?$": botc.ThreeLaws,
	}
)

func help(commands map[string]command.Command, serviceCommands []string) command.Command {
	usage := "help"
	desc := "Displays help for all known commands"

	var cmds []command.Command

	for _, cmd := range commands {
		cmds = append(cmds, cmd)
	}

	sort.Sort(sortedCommands{cmds})

	return command.NewCommand("help", usage, desc, func(args ...string) ([]byte, error) {
		response := []string{"\n"}
		for _, cmd := range cmds {
			response = append(response, fmt.Sprintf("%s - %s", cmd.Usage(), cmd.Description()))
		}
		response = append(response, serviceCommands...)
		return []byte(strings.Join(response, "\n")), nil
	})
}

func newBot(ctx *cli.Context, inputs map[string]input.Input, commands map[string]command.Command, service micro.Service) *bot {
	commands["^help$"] = help(commands, nil)

	return &bot{
		ctx:      ctx,
		exit:     make(chan bool),
		service:  service,
		commands: commands,
		inputs:   inputs,
		services: make(map[string]string),
	}
}

func (b *bot) loop(io input.Input) {
	log.Logf("[bot][loop] starting %s", io.String())

	for {
		select {
		case <-b.exit:
			log.Logf("[bot][loop] exiting %s", io.String())
			return
		default:
			if err := b.run(io); err != nil {
				log.Logf("[bot][loop] error %v", err)
				time.Sleep(time.Second)
			}
		}
	}
}

func (b *bot) process(c input.Conn, ev input.Event) error {
	args := strings.Split(string(ev.Data), " ")
	if len(args) == 0 {
		return nil
	}

	b.RLock()
	defer b.RUnlock()

	// try built in command
	for pattern, cmd := range b.commands {
		// skip if it doesn't match
		if m, err := regexp.Match(pattern, ev.Data); err != nil || !m {
			continue
		}

		// matched, exec command
		rsp, err := cmd.Exec(args...)
		if err != nil {
			rsp = []byte("error executing cmd: " + err.Error())
		}

		// send response
		return c.Send(&input.Event{
			Meta: ev.Meta,
			From: ev.To,
			To:   ev.From,
			Type: input.TextEvent,
			Data: rsp,
		})
	}

	// no built in match
	// try service commands
	service := Namespace + "." + args[0]

	// is there a service for the command?
	if _, ok := b.services[service]; !ok {
		return nil
	}

	// make service request
	req := b.service.Client().NewRequest(service, "Command.Exec", &proto.ExecRequest{
		Args: args,
	})
	rsp := &proto.ExecResponse{}

	var response []byte

	// call service
	if err := b.service.Client().Call(context.Background(), req, rsp); err != nil {
		response = []byte("error executing cmd: " + err.Error())
	} else if len(rsp.Error) > 0 {
		response = []byte("error executing cmd: " + rsp.Error)
	} else {
		response = rsp.Result
	}

	// send response
	return c.Send(&input.Event{
		Meta: ev.Meta,
		From: ev.To,
		To:   ev.From,
		Type: input.TextEvent,
		Data: response,
	})
}

func (b *bot) run(io input.Input) error {
	log.Logf("[bot][loop] connecting to %s", io.String())

	c, err := io.Stream()
	if err != nil {
		return err
	}

	for {
		select {
		case <-b.exit:
			log.Logf("[bot][loop] closing %s", io.String())
			return c.Close()
		default:
			var recvEv input.Event
			// receive input
			if err := c.Recv(&recvEv); err != nil {
				return err
			}

			// only process TextEvent
			if recvEv.Type != input.TextEvent {
				continue
			}

			if len(recvEv.Data) == 0 {
				continue
			}

			if err := b.process(c, recvEv); err != nil {
				return err
			}
		}
	}
}

func (b *bot) start() error {
	log.Log("[bot] starting")

	// Start inputs
	for _, io := range b.inputs {
		log.Logf("[bot] starting input %s", io.String())

		if err := io.Init(b.ctx); err != nil {
			return err
		}

		if err := io.Start(); err != nil {
			return err
		}

		go b.loop(io)
	}

	// start watcher
	go b.watch()

	return nil
}

func (b *bot) stop() error {
	log.Log("[bot] stopping")
	close(b.exit)

	// Stop inputs
	for _, io := range b.inputs {
		log.Logf("[bot] stopping input %s", io.String())
		if err := io.Stop(); err != nil {
			log.Logf("[bot] %v", err)
		}
	}

	return nil
}

func (b *bot) watch() {
	commands := map[string]command.Command{}
	services := map[string]string{}

	// copy commands
	b.RLock()
	for k, v := range b.commands {
		commands[k] = v
	}
	b.RUnlock()

	// getHelp retries usage and description from bot service commands
	getHelp := func(service string) (string, error) {
		// is within namespace?
		if !strings.HasPrefix(service, Namespace) {
			return "", fmt.Errorf("%s not within namespace", service)
		}

		if p := strings.TrimPrefix(service, Namespace); len(p) == 0 {
			return "", fmt.Errorf("%s not a service", service)
		}

		// get command help
		req := b.service.Client().NewRequest(service, "Command.Help", &proto.HelpRequest{})
		rsp := &proto.HelpResponse{}

		err := b.service.Client().Call(context.Background(), req, rsp)
		if err != nil {
			return "", err
		}

		return fmt.Sprintf("%s - %s", rsp.Usage, rsp.Description), nil
	}

	serviceList, err := b.service.Client().Options().Registry.ListServices()
	if err != nil {
		// log error?
		return
	}

	var serviceCommands []string

	// create service commands
	for _, service := range serviceList {
		h, err := getHelp(service.Name)
		if err != nil {
			continue
		}
		services[service.Name] = h
		serviceCommands = append(serviceCommands, h)
	}

	b.Lock()
	b.commands["^help$"] = help(commands, serviceCommands)
	b.services = services
	b.Unlock()

	w, err := b.service.Client().Options().Registry.Watch()
	if err != nil {
		// log error?
		return
	}

	go func() {
		<-b.exit
		w.Stop()
	}()

	// watch for changes to services
	for {
		res, err := w.Next()
		if err != nil {
			return
		}

		if res.Action == "delete" {
			delete(services, res.Service.Name)
		} else {
			h, err := getHelp(res.Service.Name)
			if err != nil {
				continue
			}
			services[res.Service.Name] = h
		}

		var serviceCommands []string
		for _, v := range services {
			serviceCommands = append(serviceCommands, v)
		}

		b.Lock()
		b.commands["^help$"] = help(commands, serviceCommands)
		b.services = services
		b.Unlock()
	}
}

func run(ctx *cli.Context) {
	// Init plugins
	for _, p := range Plugins() {
		p.Init(ctx)
	}

	if len(ctx.GlobalString("server_name")) > 0 {
		Name = ctx.GlobalString("server_name")
	}

	if len(ctx.String("namespace")) > 0 {
		Namespace = ctx.String("namespace")
	}

	// Parse flags
	if len(ctx.String("inputs")) == 0 {
		log.Fatal("[bot] no inputs specified")
	}

	inputs := strings.Split(ctx.String("inputs"), ",")
	if len(inputs) == 0 {
		log.Fatal("[bot] no inputs specified")
	}

	ios := make(map[string]input.Input)
	cmds := make(map[string]command.Command)

	// create built in commands
	for pattern, cmd := range commands {
		cmds[pattern] = cmd(ctx)
	}

	// take other commands
	for pattern, cmd := range command.Commands {
		if c, ok := cmds[pattern]; ok {
			log.Logf("[bot] command %s already registered for pattern %s\n", c.String(), pattern)
			continue
		}
		// register command
		cmds[pattern] = cmd
	}

	// Parse inputs
	for _, io := range inputs {
		i, ok := input.Inputs[io]
		if !ok {
			log.Logf("[bot] input %s not found\n", i)
			os.Exit(1)
		}
		ios[io] = i
	}

	// setup service
	service := micro.NewService(
		micro.Name(Name),
		micro.RegisterTTL(
			time.Duration(ctx.GlobalInt("register_ttl"))*time.Second,
		),
		micro.RegisterInterval(
			time.Duration(ctx.GlobalInt("register_interval"))*time.Second,
		),
	)

	// Start bot
	b := newBot(ctx, ios, cmds, service)

	if err := b.start(); err != nil {
		log.Logf("error starting bot %v", err)
		os.Exit(1)
	}

	// Run server
	if err := service.Run(); err != nil {
		log.Fatal(err)
	}

	// Stop bot
	if err := b.stop(); err != nil {
		log.Logf("error stopping bot %v", err)
	}
}

func Commands() []cli.Command {
	flags := []cli.Flag{
		cli.StringFlag{
			Name:   "inputs",
			Usage:  "Inputs to load on startup",
			EnvVar: "MICRO_BOT_INPUTS",
		},
		cli.StringFlag{
			Name:   "namespace",
			Usage:  "Set the namespace used by the bot to find commands e.g. com.example.bot",
			EnvVar: "MICRO_BOT_NAMESPACE",
		},
	}

	// setup input flags
	for _, input := range input.Inputs {
		flags = append(flags, input.Flags()...)
	}

	command := cli.Command{
		Name:   "bot",
		Usage:  "Run the chatops bot",
		Flags:  flags,
		Action: run,
	}

	for _, p := range Plugins() {
		if cmds := p.Commands(); len(cmds) > 0 {
			command.Subcommands = append(command.Subcommands, cmds...)
		}

		if flags := p.Flags(); len(flags) > 0 {
			command.Flags = append(command.Flags, flags...)
		}
	}

	return []cli.Command{command}
}
<commit_msg>Support starting bot without inputs<commit_after>// Package bot is a Hubot style bot that sits a microservice environment
package bot

import (
	"context"
	"fmt"
	"os"
	"regexp"
	"sort"
	"strings"
	"sync"
	"time"

	"github.com/micro/cli"
	"github.com/micro/go-micro"

	"github.com/micro/go-micro/agent/command"
	"github.com/micro/go-micro/agent/input"
	"github.com/micro/go-micro/util/log"
	botc "github.com/micro/micro/internal/command/bot"

	proto "github.com/micro/go-micro/agent/proto"

	// inputs
	_ "github.com/micro/go-micro/agent/input/discord"
	_ "github.com/micro/go-micro/agent/input/slack"
	_ "github.com/micro/go-micro/agent/input/telegram"
)

type bot struct {
	exit    chan bool
	ctx     *cli.Context
	service micro.Service

	sync.RWMutex
	inputs   map[string]input.Input
	commands map[string]command.Command
	services map[string]string
}

var (
	// Default server name
	Name = "go.micro.bot"
	// Namespace for commands
	Namespace = "go.micro.bot"
	// map pattern:command
	commands = map[string]func(*cli.Context) command.Command{
		"^echo ":                             botc.Echo,
		"^time$":                             botc.Time,
		"^hello$":                            botc.Hello,
		"^ping$":                             botc.Ping,
		"^list ":                             botc.List,
		"^get ":                              botc.Get,
		"^health ":                           botc.Health,
		"^call ":                             botc.Call,
		"^register ":                         botc.Register,
		"^deregister ":                       botc.Deregister,
		"^(the )?three laws( of robotics)?$": botc.ThreeLaws,
	}
)

func help(commands map[string]command.Command, serviceCommands []string) command.Command {
	usage := "help"
	desc := "Displays help for all known commands"

	var cmds []command.Command

	for _, cmd := range commands {
		cmds = append(cmds, cmd)
	}

	sort.Sort(sortedCommands{cmds})

	return command.NewCommand("help", usage, desc, func(args ...string) ([]byte, error) {
		response := []string{"\n"}
		for _, cmd := range cmds {
			response = append(response, fmt.Sprintf("%s - %s", cmd.Usage(), cmd.Description()))
		}
		response = append(response, serviceCommands...)
		return []byte(strings.Join(response, "\n")), nil
	})
}

func newBot(ctx *cli.Context, inputs map[string]input.Input, commands map[string]command.Command, service micro.Service) *bot {
	commands["^help$"] = help(commands, nil)

	return &bot{
		ctx:      ctx,
		exit:     make(chan bool),
		service:  service,
		commands: commands,
		inputs:   inputs,
		services: make(map[string]string),
	}
}

func (b *bot) loop(io input.Input) {
	log.Logf("[bot][loop] starting %s", io.String())

	for {
		select {
		case <-b.exit:
			log.Logf("[bot][loop] exiting %s", io.String())
			return
		default:
			if err := b.run(io); err != nil {
				log.Logf("[bot][loop] error %v", err)
				time.Sleep(time.Second)
			}
		}
	}
}

func (b *bot) process(c input.Conn, ev input.Event) error {
	args := strings.Split(string(ev.Data), " ")
	if len(args) == 0 {
		return nil
	}

	b.RLock()
	defer b.RUnlock()

	// try built in command
	for pattern, cmd := range b.commands {
		// skip if it doesn't match
		if m, err := regexp.Match(pattern, ev.Data); err != nil || !m {
			continue
		}

		// matched, exec command
		rsp, err := cmd.Exec(args...)
		if err != nil {
			rsp = []byte("error executing cmd: " + err.Error())
		}

		// send response
		return c.Send(&input.Event{
			Meta: ev.Meta,
			From: ev.To,
			To:   ev.From,
			Type: input.TextEvent,
			Data: rsp,
		})
	}

	// no built in match
	// try service commands
	service := Namespace + "." + args[0]

	// is there a service for the command?
	if _, ok := b.services[service]; !ok {
		return nil
	}

	// make service request
	req := b.service.Client().NewRequest(service, "Command.Exec", &proto.ExecRequest{
		Args: args,
	})
	rsp := &proto.ExecResponse{}

	var response []byte

	// call service
	if err := b.service.Client().Call(context.Background(), req, rsp); err != nil {
		response = []byte("error executing cmd: " + err.Error())
	} else if len(rsp.Error) > 0 {
		response = []byte("error executing cmd: " + rsp.Error)
	} else {
		response = rsp.Result
	}

	// send response
	return c.Send(&input.Event{
		Meta: ev.Meta,
		From: ev.To,
		To:   ev.From,
		Type: input.TextEvent,
		Data: response,
	})
}

func (b *bot) run(io input.Input) error {
	log.Logf("[bot][loop] connecting to %s", io.String())

	c, err := io.Stream()
	if err != nil {
		return err
	}

	for {
		select {
		case <-b.exit:
			log.Logf("[bot][loop] closing %s", io.String())
			return c.Close()
		default:
			var recvEv input.Event
			// receive input
			if err := c.Recv(&recvEv); err != nil {
				return err
			}

			// only process TextEvent
			if recvEv.Type != input.TextEvent {
				continue
			}

			if len(recvEv.Data) == 0 {
				continue
			}

			if err := b.process(c, recvEv); err != nil {
				return err
			}
		}
	}
}

func (b *bot) start() error {
	log.Log("[bot] starting")

	// Start inputs
	for _, io := range b.inputs {
		log.Logf("[bot] starting input %s", io.String())

		if err := io.Init(b.ctx); err != nil {
			return err
		}

		if err := io.Start(); err != nil {
			return err
		}

		go b.loop(io)
	}

	// start watcher
	go b.watch()

	return nil
}

func (b *bot) stop() error {
	log.Log("[bot] stopping")
	close(b.exit)

	// Stop inputs
	for _, io := range b.inputs {
		log.Logf("[bot] stopping input %s", io.String())
		if err := io.Stop(); err != nil {
			log.Logf("[bot] %v", err)
		}
	}

	return nil
}

func (b *bot) watch() {
	commands := map[string]command.Command{}
	services := map[string]string{}

	// copy commands
	b.RLock()
	for k, v := range b.commands {
		commands[k] = v
	}
	b.RUnlock()

	// getHelp retries usage and description from bot service commands
	getHelp := func(service string) (string, error) {
		// is within namespace?
		if !strings.HasPrefix(service, Namespace) {
			return "", fmt.Errorf("%s not within namespace", service)
		}

		if p := strings.TrimPrefix(service, Namespace); len(p) == 0 {
			return "", fmt.Errorf("%s not a service", service)
		}

		// get command help
		req := b.service.Client().NewRequest(service, "Command.Help", &proto.HelpRequest{})
		rsp := &proto.HelpResponse{}

		err := b.service.Client().Call(context.Background(), req, rsp)
		if err != nil {
			return "", err
		}

		return fmt.Sprintf("%s - %s", rsp.Usage, rsp.Description), nil
	}

	serviceList, err := b.service.Client().Options().Registry.ListServices()
	if err != nil {
		// log error?
		return
	}

	var serviceCommands []string

	// create service commands
	for _, service := range serviceList {
		h, err := getHelp(service.Name)
		if err != nil {
			continue
		}
		services[service.Name] = h
		serviceCommands = append(serviceCommands, h)
	}

	b.Lock()
	b.commands["^help$"] = help(commands, serviceCommands)
	b.services = services
	b.Unlock()

	w, err := b.service.Client().Options().Registry.Watch()
	if err != nil {
		// log error?
		return
	}

	go func() {
		<-b.exit
		w.Stop()
	}()

	// watch for changes to services
	for {
		res, err := w.Next()
		if err != nil {
			return
		}

		if res.Action == "delete" {
			delete(services, res.Service.Name)
		} else {
			h, err := getHelp(res.Service.Name)
			if err != nil {
				continue
			}
			services[res.Service.Name] = h
		}

		var serviceCommands []string
		for _, v := range services {
			serviceCommands = append(serviceCommands, v)
		}

		b.Lock()
		b.commands["^help$"] = help(commands, serviceCommands)
		b.services = services
		b.Unlock()
	}
}

func run(ctx *cli.Context) {
	// Init plugins
	for _, p := range Plugins() {
		p.Init(ctx)
	}

	if len(ctx.GlobalString("server_name")) > 0 {
		Name = ctx.GlobalString("server_name")
	}

	if len(ctx.String("namespace")) > 0 {
		Namespace = ctx.String("namespace")
	}

	// Parse inputs
	var inputs []string

	parts := strings.Split(ctx.String("inputs"), ",")
	for _, p := range parts {
		if len(p) > 0 {
			inputs = append(inputs, p)
		}
	}

	ios := make(map[string]input.Input)
	cmds := make(map[string]command.Command)

	// create built in commands
	for pattern, cmd := range commands {
		cmds[pattern] = cmd(ctx)
	}

	// take other commands
	for pattern, cmd := range command.Commands {
		if c, ok := cmds[pattern]; ok {
			log.Logf("[bot] command %s already registered for pattern %s\n", c.String(), pattern)
			continue
		}
		// register command
		cmds[pattern] = cmd
	}

	// Parse inputs
	for _, io := range inputs {
		if len(io) == 0 {
			continue
		}
		i, ok := input.Inputs[io]
		if !ok {
			log.Logf("[bot] input %s not found\n", i)
			os.Exit(1)
		}
		ios[io] = i
	}

	// setup service
	service := micro.NewService(
		micro.Name(Name),
		micro.RegisterTTL(
			time.Duration(ctx.GlobalInt("register_ttl"))*time.Second,
		),
		micro.RegisterInterval(
			time.Duration(ctx.GlobalInt("register_interval"))*time.Second,
		),
	)

	// Start bot
	b := newBot(ctx, ios, cmds, service)

	if err := b.start(); err != nil {
		log.Logf("error starting bot %v", err)
		os.Exit(1)
	}

	// Run server
	if err := service.Run(); err != nil {
		log.Fatal(err)
	}

	// Stop bot
	if err := b.stop(); err != nil {
		log.Logf("error stopping bot %v", err)
	}
}

func Commands() []cli.Command {
	flags := []cli.Flag{
		cli.StringFlag{
			Name:   "inputs",
			Usage:  "Inputs to load on startup",
			EnvVar: "MICRO_BOT_INPUTS",
		},
		cli.StringFlag{
			Name:   "namespace",
			Usage:  "Set the namespace used by the bot to find commands e.g. com.example.bot",
			EnvVar: "MICRO_BOT_NAMESPACE",
		},
	}

	// setup input flags
	for _, input := range input.Inputs {
		flags = append(flags, input.Flags()...)
	}

	command := cli.Command{
		Name:   "bot",
		Usage:  "Run the chatops bot",
		Flags:  flags,
		Action: run,
	}

	for _, p := range Plugins() {
		if cmds := p.Commands(); len(cmds) > 0 {
			command.Subcommands = append(command.Subcommands, cmds...)
		}

		if flags := p.Flags(); len(flags) > 0 {
			command.Flags = append(command.Flags, flags...)
		}
	}

	return []cli.Command{command}
}
<|endoftext|>


<commit_before>// Package bot provides the interfaces for creating a chatops
// bot.
package bot

/* bot.go defines core data structures and public methods for startup.
   handler.go has the methods for callbacks from the connector, */

import (
	"fmt"
	"log"
	"math/rand"
	"regexp"
	"sync"
	"time"
)

// Version is the current version of Gopherbot
var Version = "1.0.0-dev"

// mkdist.* creates a temporary commit.go that sets commit to the current
// git commit in an init() function
var commit = "(manual build)"

var globalLock sync.RWMutex
var random *rand.Rand

var connectors = make(map[string]func(Handler, *log.Logger) Connector)

// RegisterConnector should be called in an init function to register a type
// of connector. Currently only Slack is implemented.
func RegisterConnector(name string, connstarter func(Handler, *log.Logger) Connector) {
	if stopRegistrations {
		return
	}
	if connectors[name] != nil {
		log.Fatal("Attempted registration of duplicate connector:", name)
	}
	connectors[name] = connstarter
}

// robot holds all the interal data relevant to the Bot. Most of it is populated
// by loadConfig, other stuff is populated by the connector.
var robot struct {
	Connector                           // Connector interface, implemented by each specific protocol
	localPath          string           // Directory for local files overriding default config
	installPath        string           // Path to the bot's installation directory
	adminUsers         []string         // List of users with access to administrative commands
	alias              rune             // single-char alias for addressing the bot
	name               string           // e.g. "Gort"
	fullName           string           // e.g. "Robbie Robot"
	adminContact       string           // who to contact for problems with the robot.
	email              string           // the from: when the robot sends email
	mailConf           botMailer        // configuration to use when sending email
	ignoreUsers        []string         // list of users to never listen to, like other bots
	preRegex           *regexp.Regexp   // regex for matching prefixed commands, e.g. "Gort, drop your weapon"
	postRegex          *regexp.Regexp   // regex for matching, e.g. "open the pod bay doors, hal"
	joinChannels       []string         // list of channels to join
	defaultAllowDirect bool             // whether plugins are available in DM by default
	plugChannels       []string         // list of channels where plugins are active by default
	sync.RWMutex                        // for safe updating of bot data structures
	protocol           string           // Name of the protocol, e.g. "slack"
	brainProvider      string           // Type of Brain provider to use
	brain              SimpleBrain      // Interface for robot to Store and Retrieve data
	defaultElevator    string           // Plugin name for performing elevation
	defaultAuthorizer  string           // Plugin name for performing authorization
	externalPlugins    []externalPlugin // List of external plugins to load
	port               string           // Localhost port to listen on
	logger             *log.Logger      // Where to log to
}

//var robot *robotcfg

// newBot instantiates the one and only instance of a Gobot, and loads
// configuration.
func newBot(cpath, epath string, logger *log.Logger) error {
	globalLock.Lock()
	// Prevent plugin registration after program init
	stopRegistrations = true
	// Seed the pseudo-random number generator, for plugin IDs, RandomString, etc.
	random = rand.New(rand.NewSource(time.Now().UnixNano()))

	globalLock.Unlock()

	robot.localPath = cpath
	robot.installPath = epath
	robot.logger = logger

	handle := handler{}
	if err := loadConfig(); err != nil {
		return err
	}

	if len(robot.brainProvider) > 0 {
		if bprovider, ok := brains[robot.brainProvider]; !ok {
			Log(Fatal, fmt.Sprintf("No provider registered for brain: \"%s\"", robot.brainProvider))
		} else {
			robot.brain = bprovider(handle, logger)
		}
	}
	return nil
}

// Init is called after the bot is connected.
func botInit(c Connector) {
	robot.Lock()
	if robot.Connector != nil {
		robot.Unlock()
		return
	}
	robot.Connector = c
	robot.Unlock()
	go listenHTTPJSON()
	var cl []string
	robot.RLock()
	cl = append(cl, robot.joinChannels...)
	robot.RUnlock()
	for _, channel := range cl {
		robot.JoinChannel(channel)
	}
	initializePlugins()
}
<commit_msg>Bump version to test<commit_after>// Package bot provides the interfaces for creating a chatops
// bot.
package bot

/* bot.go defines core data structures and public methods for startup.
   handler.go has the methods for callbacks from the connector, */

import (
	"fmt"
	"log"
	"math/rand"
	"regexp"
	"sync"
	"time"
)

// Version is the current version of Gopherbot
var Version = "1.0.0-test"

// mkdist.* creates a temporary commit.go that sets commit to the current
// git commit in an init() function
var commit = "(manual build)"

var globalLock sync.RWMutex
var random *rand.Rand

var connectors = make(map[string]func(Handler, *log.Logger) Connector)

// RegisterConnector should be called in an init function to register a type
// of connector. Currently only Slack is implemented.
func RegisterConnector(name string, connstarter func(Handler, *log.Logger) Connector) {
	if stopRegistrations {
		return
	}
	if connectors[name] != nil {
		log.Fatal("Attempted registration of duplicate connector:", name)
	}
	connectors[name] = connstarter
}

// robot holds all the interal data relevant to the Bot. Most of it is populated
// by loadConfig, other stuff is populated by the connector.
var robot struct {
	Connector                           // Connector interface, implemented by each specific protocol
	localPath          string           // Directory for local files overriding default config
	installPath        string           // Path to the bot's installation directory
	adminUsers         []string         // List of users with access to administrative commands
	alias              rune             // single-char alias for addressing the bot
	name               string           // e.g. "Gort"
	fullName           string           // e.g. "Robbie Robot"
	adminContact       string           // who to contact for problems with the robot.
	email              string           // the from: when the robot sends email
	mailConf           botMailer        // configuration to use when sending email
	ignoreUsers        []string         // list of users to never listen to, like other bots
	preRegex           *regexp.Regexp   // regex for matching prefixed commands, e.g. "Gort, drop your weapon"
	postRegex          *regexp.Regexp   // regex for matching, e.g. "open the pod bay doors, hal"
	joinChannels       []string         // list of channels to join
	defaultAllowDirect bool             // whether plugins are available in DM by default
	plugChannels       []string         // list of channels where plugins are active by default
	sync.RWMutex                        // for safe updating of bot data structures
	protocol           string           // Name of the protocol, e.g. "slack"
	brainProvider      string           // Type of Brain provider to use
	brain              SimpleBrain      // Interface for robot to Store and Retrieve data
	defaultElevator    string           // Plugin name for performing elevation
	defaultAuthorizer  string           // Plugin name for performing authorization
	externalPlugins    []externalPlugin // List of external plugins to load
	port               string           // Localhost port to listen on
	logger             *log.Logger      // Where to log to
}

//var robot *robotcfg

// newBot instantiates the one and only instance of a Gobot, and loads
// configuration.
func newBot(cpath, epath string, logger *log.Logger) error {
	globalLock.Lock()
	// Prevent plugin registration after program init
	stopRegistrations = true
	// Seed the pseudo-random number generator, for plugin IDs, RandomString, etc.
	random = rand.New(rand.NewSource(time.Now().UnixNano()))

	globalLock.Unlock()

	robot.localPath = cpath
	robot.installPath = epath
	robot.logger = logger

	handle := handler{}
	if err := loadConfig(); err != nil {
		return err
	}

	if len(robot.brainProvider) > 0 {
		if bprovider, ok := brains[robot.brainProvider]; !ok {
			Log(Fatal, fmt.Sprintf("No provider registered for brain: \"%s\"", robot.brainProvider))
		} else {
			robot.brain = bprovider(handle, logger)
		}
	}
	return nil
}

// Init is called after the bot is connected.
func botInit(c Connector) {
	robot.Lock()
	if robot.Connector != nil {
		robot.Unlock()
		return
	}
	robot.Connector = c
	robot.Unlock()
	go listenHTTPJSON()
	var cl []string
	robot.RLock()
	cl = append(cl, robot.joinChannels...)
	robot.RUnlock()
	for _, channel := range cl {
		robot.JoinChannel(channel)
	}
	initializePlugins()
}
<|endoftext|>


<commit_before>package cred

import (
	"bufio"
	"fmt"
	"io"
	"log"
	"os"
	"path"
	"strings"
)

type credentialsFile struct {
	groups []*credentialsGroup
}

func (cf *credentialsFile) addGroup(g *credentialsGroup) {
	for i, v := range cf.groups {
		if v.profile == g.profile {
			cf.groups[i] = g
			return
		}
	}
	cf.groups = append(cf.groups, g)
}

func (cf *credentialsFile) write(w io.Writer) {
	for _, g := range cf.groups {
		fmt.Fprintf(w, "[%s]\n", g.profile)
		for _, line := range g.lines {
			fmt.Fprintf(w, "%s = %s\n", line.key, line.value)
		}
	}
}

type propertyLine struct {
	key   string
	value string
}
type credentialsGroup struct {
	profile string
	lines   []*propertyLine
}

func credentialsFilePath() string {
	return path.Join(os.Getenv("HOME"), ".aws/credentials")
}
func SaveCredentials(profile, keyId, secret, token string) {
	f, err := os.OpenFile(credentialsFilePath(), os.O_CREATE|os.O_RDWR, 0600)
	if err != nil {
		log.Println(err)
	}
	defer f.Close()

	cf := readCredentials(f)
	cf.addGroup(newCredentialsGroup(profile, keyId, secret, token))

	err = f.Truncate(0)
	if err != nil {
		log.Println(err)
	}
	_, err = f.Seek(0, 0)
	if err != nil {
		log.Println(err)
	}

	cf.write(f)
}

func newCredentialsGroup(profile, keyId, secret, token string) *credentialsGroup {
	g := &credentialsGroup{
		profile: profile,
		lines: []*propertyLine{
			&propertyLine{"aws_access_key_id", keyId},
			&propertyLine{"aws_secret_access_key", secret},
		},
	}
	if token != "" {
		g.lines = append(g.lines, &propertyLine{"aws_session_token", token})
	}
	return g
}

func readCredentials(r io.Reader) *credentialsFile {
	cf := &credentialsFile{[]*credentialsGroup{}}
	var gr *credentialsGroup
	sc := bufio.NewScanner(r)
	for sc.Scan() {
		line := sc.Text()
		line = strings.TrimSpace(line)
		if len(line) == 0 {
			continue
		}

		if line[0] == '[' {
			gr = &credentialsGroup{
				profile: line[1 : len(line)-1],
				lines:   []*propertyLine{},
			}
			cf.groups = append(cf.groups, gr)
		} else {
			pp := strings.Split(line, " = ")
			gr.lines = append(gr.lines, &propertyLine{pp[0], pp[1]})
		}
	}
	return cf
}

func GetMainAccountKeyId(profileName string) string {
	f, err := os.Open(credentialsFilePath())
	if err != nil {
		log.Println(err)
	}
	defer f.Close()

	cf := readCredentials(f)
	for _, g := range cf.groups {
		if g.profile == profileName {
			for _, l := range g.lines {
				if l.key == "aws_access_key_id" {
					return l.value
				}
			}
		}
	}
	log.Fatalf("Credentials file does not contain profile '%s'\n", profileName)
	return ""
}
<commit_msg>Variables in .aws/credentials can be separated by "=" without spaces<commit_after>package cred

import (
	"bufio"
	"fmt"
	"io"
	"log"
	"os"
	"path"
	"strings"
)

type credentialsFile struct {
	groups []*credentialsGroup
}

func (cf *credentialsFile) addGroup(g *credentialsGroup) {
	for i, v := range cf.groups {
		if v.profile == g.profile {
			cf.groups[i] = g
			return
		}
	}
	cf.groups = append(cf.groups, g)
}

func (cf *credentialsFile) write(w io.Writer) {
	for _, g := range cf.groups {
		fmt.Fprintf(w, "[%s]\n", g.profile)
		for _, line := range g.lines {
			fmt.Fprintf(w, "%s = %s\n", line.key, line.value)
		}
	}
}

type propertyLine struct {
	key   string
	value string
}
type credentialsGroup struct {
	profile string
	lines   []*propertyLine
}

func credentialsFilePath() string {
	return path.Join(os.Getenv("HOME"), ".aws/credentials")
}
func SaveCredentials(profile, keyId, secret, token string) {
	f, err := os.OpenFile(credentialsFilePath(), os.O_CREATE|os.O_RDWR, 0600)
	if err != nil {
		log.Println(err)
	}
	defer f.Close()

	cf := readCredentials(f)
	cf.addGroup(newCredentialsGroup(profile, keyId, secret, token))

	err = f.Truncate(0)
	if err != nil {
		log.Println(err)
	}
	_, err = f.Seek(0, 0)
	if err != nil {
		log.Println(err)
	}

	cf.write(f)
}

func newCredentialsGroup(profile, keyId, secret, token string) *credentialsGroup {
	g := &credentialsGroup{
		profile: profile,
		lines: []*propertyLine{
			&propertyLine{"aws_access_key_id", keyId},
			&propertyLine{"aws_secret_access_key", secret},
		},
	}
	if token != "" {
		g.lines = append(g.lines, &propertyLine{"aws_session_token", token})
	}
	return g
}

func readCredentials(r io.Reader) *credentialsFile {
	cf := &credentialsFile{[]*credentialsGroup{}}
	var gr *credentialsGroup
	sc := bufio.NewScanner(r)
	for sc.Scan() {
		line := sc.Text()
		line = strings.TrimSpace(line)
		if len(line) == 0 {
			continue
		}

		if line[0] == '[' {
			gr = &credentialsGroup{
				profile: line[1 : len(line)-1],
				lines:   []*propertyLine{},
			}
			cf.groups = append(cf.groups, gr)
		} else {
			pp := strings.Split(line, "=")
			for i, v := range pp {
				pp[i] = strings.TrimSpace(v)
			}
			gr.lines = append(gr.lines, &propertyLine{pp[0], pp[1]})
		}
	}
	return cf
}

func GetMainAccountKeyId(profileName string) string {
	f, err := os.Open(credentialsFilePath())
	if err != nil {
		log.Println(err)
	}
	defer f.Close()

	cf := readCredentials(f)
	for _, g := range cf.groups {
		if g.profile == profileName {
			for _, l := range g.lines {
				if l.key == "aws_access_key_id" {
					return l.value
				}
			}
		}
	}
	log.Fatalf("Credentials file does not contain profile '%s'\n", profileName)
	return ""
}
<|endoftext|>


<commit_before><commit_msg>cmd/go: fix build -n output when using swig<commit_after><|endoftext|>


<commit_before>package server

import (
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"

	"github.com/Sirupsen/logrus"
	"github.com/docker/docker/pkg/stringid"
	"github.com/kubernetes-incubator/cri-o/oci"
	"github.com/kubernetes-incubator/cri-o/utils"
	"github.com/opencontainers/runtime-tools/generate"
	"golang.org/x/net/context"
	pb "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime"
)

type sandbox struct {
	id         string
	name       string
	logDir     string
	labels     map[string]string
	containers oci.Store
}

const (
	podInfraRootfs      = "/var/lib/ocid/graph/vfs/pause"
	podDefaultNamespace = "default"
)

func (s *sandbox) addContainer(c *oci.Container) {
	s.containers.Add(c.Name(), c)
}

func (s *sandbox) getContainer(name string) *oci.Container {
	return s.containers.Get(name)
}

func (s *sandbox) removeContainer(c *oci.Container) {
	s.containers.Delete(c.Name())
}

func (s *Server) generatePodIDandName(name, namespace string) (string, string, error) {
	var (
		err error
		id  = stringid.GenerateNonCryptoID()
	)
	if namespace == "" {
		namespace = podDefaultNamespace
	}
	if name, err = s.reservePodName(id, namespace+"-"+name); err != nil {
		return "", "", err
	}
	return id, name, err
}

// RunPodSandbox creates and runs a pod-level sandbox.
func (s *Server) RunPodSandbox(ctx context.Context, req *pb.RunPodSandboxRequest) (*pb.RunPodSandboxResponse, error) {
	// process req.Name
	name := req.GetConfig().GetMetadata().GetName()
	if name == "" {
		return nil, fmt.Errorf("PodSandboxConfig.Name should not be empty")
	}

	namespace := req.GetConfig().GetMetadata().GetNamespace()

	var err error
	id, name, err := s.generatePodIDandName(name, namespace)
	if err != nil {
		return nil, err
	}
	podSandboxDir := filepath.Join(s.sandboxDir, id)
	if _, err = os.Stat(podSandboxDir); err == nil {
		return nil, fmt.Errorf("pod sandbox (%s) already exists", podSandboxDir)
	}

	if err = os.MkdirAll(podSandboxDir, 0755); err != nil {
		return nil, err
	}

	defer func() {
		if err != nil {
			if err2 := os.RemoveAll(podSandboxDir); err2 != nil {
				logrus.Warnf("couldn't cleanup podSandboxDir %s: %v", podSandboxDir, err2)
			}
		}
	}()

	// creates a spec Generator with the default spec.
	g := generate.New()

	podInfraRootfs := filepath.Join(s.root, "graph/vfs/pause")
	// setup defaults for the pod sandbox
	g.SetRootPath(filepath.Join(podInfraRootfs, "rootfs"))
	g.SetRootReadonly(true)
	g.SetProcessArgs([]string{"/pause"})

	// set hostname
	hostname := req.GetConfig().GetHostname()
	if hostname != "" {
		g.SetHostname(hostname)
	}

	// set log directory
	logDir := req.GetConfig().GetLogDirectory()
	if logDir == "" {
		logDir = fmt.Sprintf("/var/log/ocid/pods/%s", id)
	}

	// set DNS options
	dnsServers := req.GetConfig().GetDnsOptions().GetServers()
	dnsSearches := req.GetConfig().GetDnsOptions().GetSearches()
	resolvPath := fmt.Sprintf("%s/resolv.conf", podSandboxDir)
	err = parseDNSOptions(dnsServers, dnsSearches, resolvPath)
	if err != nil {
		err1 := removeFile(resolvPath)
		if err1 != nil {
			err = err1
			return nil, fmt.Errorf("%v; failed to remove %s: %v", err, resolvPath, err1)
		}
		return nil, err
	}

	g.AddBindMount(resolvPath, "/etc/resolv.conf", "ro")

	// add labels
	labels := req.GetConfig().GetLabels()
	labelsJSON, err := json.Marshal(labels)
	if err != nil {
		return nil, err
	}
	g.AddAnnotation("ocid/labels", string(labelsJSON))
	g.AddAnnotation("ocid/log_path", logDir)
	g.AddAnnotation("ocid/name", name)
	containerName := name + "-infra"
	g.AddAnnotation("ocid/container_name", containerName)
	s.addSandbox(&sandbox{
		id:         id,
		name:       name,
		logDir:     logDir,
		labels:     labels,
		containers: oci.NewMemoryStore(),
	})

	annotations := req.GetConfig().GetAnnotations()
	for k, v := range annotations {
		g.AddAnnotation(k, v)
	}

	// setup cgroup settings
	cgroupParent := req.GetConfig().GetLinux().GetCgroupParent()
	if cgroupParent != "" {
		g.SetLinuxCgroupsPath(cgroupParent)
	}

	// set up namespaces
	if req.GetConfig().GetLinux().GetNamespaceOptions().GetHostNetwork() {
		err = g.RemoveLinuxNamespace("network")
		if err != nil {
			return nil, err
		}
	}

	if req.GetConfig().GetLinux().GetNamespaceOptions().GetHostPid() {
		err = g.RemoveLinuxNamespace("pid")
		if err != nil {
			return nil, err
		}
	}

	if req.GetConfig().GetLinux().GetNamespaceOptions().GetHostIpc() {
		err = g.RemoveLinuxNamespace("ipc")
		if err != nil {
			return nil, err
		}
	}

	err = g.SaveToFile(filepath.Join(podSandboxDir, "config.json"))
	if err != nil {
		return nil, err
	}

	if _, err = os.Stat(podInfraRootfs); err != nil {
		if os.IsNotExist(err) {
			// TODO: Replace by rootfs creation API when it is ready
			if err = utils.CreateFakeRootfs(podInfraRootfs, "docker://kubernetes/pause"); err != nil {
				return nil, err
			}
		} else {
			return nil, err
		}
	}

	container, err := oci.NewContainer(containerName, podSandboxDir, podSandboxDir, labels, id, false)
	if err != nil {
		return nil, err
	}

	if err = s.runtime.CreateContainer(container); err != nil {
		return nil, err
	}

	if err = s.runtime.UpdateStatus(container); err != nil {
		return nil, err
	}

	// setup the network
	podNamespace := ""
	netnsPath, err := container.NetNsPath()
	if err != nil {
		return nil, err
	}
	if err = s.netPlugin.SetUpPod(netnsPath, podNamespace, id, containerName); err != nil {
		return nil, fmt.Errorf("failed to create network for container %s in sandbox %s: %v", containerName, id, err)
	}

	if err = s.runtime.StartContainer(container); err != nil {
		return nil, err
	}

	s.addContainer(container)

	if err = s.podIDIndex.Add(id); err != nil {
		return nil, err
	}

	if err = s.runtime.UpdateStatus(container); err != nil {
		return nil, err
	}

	return &pb.RunPodSandboxResponse{PodSandboxId: &id}, nil
}

// StopPodSandbox stops the sandbox. If there are any running containers in the
// sandbox, they should be force terminated.
func (s *Server) StopPodSandbox(ctx context.Context, req *pb.StopPodSandboxRequest) (*pb.StopPodSandboxResponse, error) {
	sbID := req.GetPodSandboxId()
	if sbID == "" {
		return nil, fmt.Errorf("PodSandboxId should not be empty")
	}

	sandboxID, err := s.podIDIndex.Get(sbID)
	if err != nil {
		return nil, fmt.Errorf("PodSandbox with ID starting with %s not found: %v", sbID, err)
	}

	sb := s.getSandbox(sandboxID)
	if sb == nil {
		return nil, fmt.Errorf("specified sandbox not found: %s", sandboxID)
	}

	podInfraContainer := sb.name + "-infra"
	for _, c := range sb.containers.List() {
		if podInfraContainer == c.Name() {
			podNamespace := ""
			netnsPath, err := c.NetNsPath()
			if err != nil {
				return nil, err
			}
			if err := s.netPlugin.TearDownPod(netnsPath, podNamespace, sandboxID, podInfraContainer); err != nil {
				return nil, fmt.Errorf("failed to destroy network for container %s in sandbox %s: %v", c.Name(), sandboxID, err)
			}
		}
		cStatus := s.runtime.ContainerStatus(c)
		if cStatus.Status != "stopped" {
			if err := s.runtime.StopContainer(c); err != nil {
				return nil, fmt.Errorf("failed to stop container %s in sandbox %s: %v", c.Name(), sandboxID, err)
			}
		}
	}

	return &pb.StopPodSandboxResponse{}, nil
}

// RemovePodSandbox deletes the sandbox. If there are any running containers in the
// sandbox, they should be force deleted.
func (s *Server) RemovePodSandbox(ctx context.Context, req *pb.RemovePodSandboxRequest) (*pb.RemovePodSandboxResponse, error) {
	sbID := req.GetPodSandboxId()
	if sbID == "" {
		return nil, fmt.Errorf("PodSandboxId should not be empty")
	}

	sandboxID, err := s.podIDIndex.Get(sbID)
	if err != nil {
		return nil, fmt.Errorf("PodSandbox with ID starting with %s not found: %v", sbID, err)
	}

	sb := s.getSandbox(sandboxID)
	if sb == nil {
		return nil, fmt.Errorf("specified sandbox not found: %s", sandboxID)
	}

	podInfraContainerName := sb.name + "-infra"
	var podInfraContainer *oci.Container

	// Delete all the containers in the sandbox
	for _, c := range sb.containers.List() {
		if err := s.runtime.UpdateStatus(c); err != nil {
			return nil, fmt.Errorf("failed to update container state: %v", err)
		}

		cState := s.runtime.ContainerStatus(c)
		if cState.Status == ContainerStateCreated || cState.Status == ContainerStateRunning {
			if err := s.runtime.StopContainer(c); err != nil {
				return nil, fmt.Errorf("failed to stop container %s: %v", c.Name(), err)
			}
		}

		if err := s.runtime.DeleteContainer(c); err != nil {
			return nil, fmt.Errorf("failed to delete container %s in sandbox %s: %v", c.Name(), sandboxID, err)
		}

		if podInfraContainerName == c.Name() {
			podInfraContainer = c
			continue
		}

		containerDir := filepath.Join(s.runtime.ContainerDir(), c.Name())
		if err := os.RemoveAll(containerDir); err != nil {
			return nil, fmt.Errorf("failed to remove container %s directory: %v", c.Name(), err)
		}

		s.removeContainer(c)
	}

	// Remove the files related to the sandbox
	podSandboxDir := filepath.Join(s.sandboxDir, sandboxID)
	if err := os.RemoveAll(podSandboxDir); err != nil {
		return nil, fmt.Errorf("failed to remove sandbox %s directory: %v", sandboxID, err)
	}
	s.removeContainer(podInfraContainer)

	s.releasePodName(sb.name)
	s.removeSandbox(sandboxID)

	return &pb.RemovePodSandboxResponse{}, nil
}

// PodSandboxStatus returns the Status of the PodSandbox.
func (s *Server) PodSandboxStatus(ctx context.Context, req *pb.PodSandboxStatusRequest) (*pb.PodSandboxStatusResponse, error) {
	sbID := req.GetPodSandboxId()
	if sbID == "" {
		return nil, fmt.Errorf("PodSandboxId should not be empty")
	}

	sandboxID, err := s.podIDIndex.Get(sbID)
	if err != nil {
		return nil, fmt.Errorf("PodSandbox with ID starting with %s not found: %v", sbID, err)
	}

	sb := s.getSandbox(sandboxID)
	if sb == nil {
		return nil, fmt.Errorf("specified sandbox not found: %s", sandboxID)
	}

	podInfraContainerName := sb.name + "-infra"
	podInfraContainer := sb.getContainer(podInfraContainerName)
	if err = s.runtime.UpdateStatus(podInfraContainer); err != nil {
		return nil, err
	}

	cState := s.runtime.ContainerStatus(podInfraContainer)
	created := cState.Created.Unix()

	netNsPath, err := podInfraContainer.NetNsPath()
	if err != nil {
		return nil, err
	}
	podNamespace := ""
	ip, err := s.netPlugin.GetContainerNetworkStatus(netNsPath, podNamespace, sandboxID, podInfraContainerName)
	if err != nil {
		// ignore the error on network status
		ip = ""
	}

	rStatus := pb.PodSandBoxState_NOTREADY
	if cState.Status == ContainerStateRunning {
		rStatus = pb.PodSandBoxState_READY
	}

	return &pb.PodSandboxStatusResponse{
		Status: &pb.PodSandboxStatus{
			Id:        &sbID,
			CreatedAt: int64Ptr(created),
			Linux: &pb.LinuxPodSandboxStatus{
				Namespaces: &pb.Namespace{
					Network: sPtr(netNsPath),
				},
			},
			Network: &pb.PodSandboxNetworkStatus{Ip: &ip},
			State:   &rStatus,
		},
	}, nil
}

// ListPodSandbox returns a list of SandBoxes.
func (s *Server) ListPodSandbox(context.Context, *pb.ListPodSandboxRequest) (*pb.ListPodSandboxResponse, error) {
	var pods []*pb.PodSandbox
	for _, sb := range s.state.sandboxes {
		podInfraContainerName := sb.name + "-infra"
		podInfraContainer := sb.getContainer(podInfraContainerName)
		if podInfraContainer == nil {
			// this can't really happen, but if it does because of a bug
			// it's better not to panic
			continue
		}
		if err := s.runtime.UpdateStatus(podInfraContainer); err != nil {
			return nil, err
		}
		cState := s.runtime.ContainerStatus(podInfraContainer)
		created := cState.Created.Unix()
		rStatus := pb.PodSandBoxState_NOTREADY
		if cState.Status == ContainerStateRunning {
			rStatus = pb.PodSandBoxState_READY
		}

		pod := &pb.PodSandbox{
			Id:        &sb.id,
			CreatedAt: int64Ptr(created),
			State:     &rStatus,
		}

		pods = append(pods, pod)
	}

	return &pb.ListPodSandboxResponse{
		Items: pods,
	}, nil
}
<commit_msg>Add annotations for pods<commit_after>package server

import (
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"

	"github.com/Sirupsen/logrus"
	"github.com/docker/docker/pkg/stringid"
	"github.com/kubernetes-incubator/cri-o/oci"
	"github.com/kubernetes-incubator/cri-o/utils"
	"github.com/opencontainers/runtime-tools/generate"
	"golang.org/x/net/context"
	pb "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime"
)

type sandbox struct {
	id          string
	name        string
	logDir      string
	labels      map[string]string
	annotations map[string]string
	containers  oci.Store
}

const (
	podInfraRootfs      = "/var/lib/ocid/graph/vfs/pause"
	podDefaultNamespace = "default"
)

func (s *sandbox) addContainer(c *oci.Container) {
	s.containers.Add(c.Name(), c)
}

func (s *sandbox) getContainer(name string) *oci.Container {
	return s.containers.Get(name)
}

func (s *sandbox) removeContainer(c *oci.Container) {
	s.containers.Delete(c.Name())
}

func (s *Server) generatePodIDandName(name, namespace string) (string, string, error) {
	var (
		err error
		id  = stringid.GenerateNonCryptoID()
	)
	if namespace == "" {
		namespace = podDefaultNamespace
	}
	if name, err = s.reservePodName(id, namespace+"-"+name); err != nil {
		return "", "", err
	}
	return id, name, err
}

// RunPodSandbox creates and runs a pod-level sandbox.
func (s *Server) RunPodSandbox(ctx context.Context, req *pb.RunPodSandboxRequest) (*pb.RunPodSandboxResponse, error) {
	// process req.Name
	name := req.GetConfig().GetMetadata().GetName()
	if name == "" {
		return nil, fmt.Errorf("PodSandboxConfig.Name should not be empty")
	}

	namespace := req.GetConfig().GetMetadata().GetNamespace()

	var err error
	id, name, err := s.generatePodIDandName(name, namespace)
	if err != nil {
		return nil, err
	}
	podSandboxDir := filepath.Join(s.sandboxDir, id)
	if _, err = os.Stat(podSandboxDir); err == nil {
		return nil, fmt.Errorf("pod sandbox (%s) already exists", podSandboxDir)
	}

	if err = os.MkdirAll(podSandboxDir, 0755); err != nil {
		return nil, err
	}

	defer func() {
		if err != nil {
			if err2 := os.RemoveAll(podSandboxDir); err2 != nil {
				logrus.Warnf("couldn't cleanup podSandboxDir %s: %v", podSandboxDir, err2)
			}
		}
	}()

	// creates a spec Generator with the default spec.
	g := generate.New()

	podInfraRootfs := filepath.Join(s.root, "graph/vfs/pause")
	// setup defaults for the pod sandbox
	g.SetRootPath(filepath.Join(podInfraRootfs, "rootfs"))
	g.SetRootReadonly(true)
	g.SetProcessArgs([]string{"/pause"})

	// set hostname
	hostname := req.GetConfig().GetHostname()
	if hostname != "" {
		g.SetHostname(hostname)
	}

	// set log directory
	logDir := req.GetConfig().GetLogDirectory()
	if logDir == "" {
		logDir = fmt.Sprintf("/var/log/ocid/pods/%s", id)
	}

	// set DNS options
	dnsServers := req.GetConfig().GetDnsOptions().GetServers()
	dnsSearches := req.GetConfig().GetDnsOptions().GetSearches()
	resolvPath := fmt.Sprintf("%s/resolv.conf", podSandboxDir)
	err = parseDNSOptions(dnsServers, dnsSearches, resolvPath)
	if err != nil {
		err1 := removeFile(resolvPath)
		if err1 != nil {
			err = err1
			return nil, fmt.Errorf("%v; failed to remove %s: %v", err, resolvPath, err1)
		}
		return nil, err
	}

	g.AddBindMount(resolvPath, "/etc/resolv.conf", "ro")

	// add labels
	labels := req.GetConfig().GetLabels()
	labelsJSON, err := json.Marshal(labels)
	if err != nil {
		return nil, err
	}

	// add annotations
	annotations := req.GetConfig().GetAnnotations()
	annotationsJSON, err := json.Marshal(annotations)
	if err != nil {
		return nil, err
	}
	g.AddAnnotation("ocid/labels", string(labelsJSON))
	g.AddAnnotation("ocid/annotations", string(annotationsJSON))
	g.AddAnnotation("ocid/log_path", logDir)
	g.AddAnnotation("ocid/name", name)
	containerName := name + "-infra"
	g.AddAnnotation("ocid/container_name", containerName)
	s.addSandbox(&sandbox{
		id:          id,
		name:        name,
		logDir:      logDir,
		labels:      labels,
		annotations: annotations,
		containers:  oci.NewMemoryStore(),
	})

	for k, v := range annotations {
		g.AddAnnotation(k, v)
	}

	// setup cgroup settings
	cgroupParent := req.GetConfig().GetLinux().GetCgroupParent()
	if cgroupParent != "" {
		g.SetLinuxCgroupsPath(cgroupParent)
	}

	// set up namespaces
	if req.GetConfig().GetLinux().GetNamespaceOptions().GetHostNetwork() {
		err = g.RemoveLinuxNamespace("network")
		if err != nil {
			return nil, err
		}
	}

	if req.GetConfig().GetLinux().GetNamespaceOptions().GetHostPid() {
		err = g.RemoveLinuxNamespace("pid")
		if err != nil {
			return nil, err
		}
	}

	if req.GetConfig().GetLinux().GetNamespaceOptions().GetHostIpc() {
		err = g.RemoveLinuxNamespace("ipc")
		if err != nil {
			return nil, err
		}
	}

	err = g.SaveToFile(filepath.Join(podSandboxDir, "config.json"))
	if err != nil {
		return nil, err
	}

	if _, err = os.Stat(podInfraRootfs); err != nil {
		if os.IsNotExist(err) {
			// TODO: Replace by rootfs creation API when it is ready
			if err = utils.CreateFakeRootfs(podInfraRootfs, "docker://kubernetes/pause"); err != nil {
				return nil, err
			}
		} else {
			return nil, err
		}
	}

	container, err := oci.NewContainer(containerName, podSandboxDir, podSandboxDir, labels, id, false)
	if err != nil {
		return nil, err
	}

	if err = s.runtime.CreateContainer(container); err != nil {
		return nil, err
	}

	if err = s.runtime.UpdateStatus(container); err != nil {
		return nil, err
	}

	// setup the network
	podNamespace := ""
	netnsPath, err := container.NetNsPath()
	if err != nil {
		return nil, err
	}
	if err = s.netPlugin.SetUpPod(netnsPath, podNamespace, id, containerName); err != nil {
		return nil, fmt.Errorf("failed to create network for container %s in sandbox %s: %v", containerName, id, err)
	}

	if err = s.runtime.StartContainer(container); err != nil {
		return nil, err
	}

	s.addContainer(container)

	if err = s.podIDIndex.Add(id); err != nil {
		return nil, err
	}

	if err = s.runtime.UpdateStatus(container); err != nil {
		return nil, err
	}

	return &pb.RunPodSandboxResponse{PodSandboxId: &id}, nil
}

// StopPodSandbox stops the sandbox. If there are any running containers in the
// sandbox, they should be force terminated.
func (s *Server) StopPodSandbox(ctx context.Context, req *pb.StopPodSandboxRequest) (*pb.StopPodSandboxResponse, error) {
	sbID := req.GetPodSandboxId()
	if sbID == "" {
		return nil, fmt.Errorf("PodSandboxId should not be empty")
	}

	sandboxID, err := s.podIDIndex.Get(sbID)
	if err != nil {
		return nil, fmt.Errorf("PodSandbox with ID starting with %s not found: %v", sbID, err)
	}

	sb := s.getSandbox(sandboxID)
	if sb == nil {
		return nil, fmt.Errorf("specified sandbox not found: %s", sandboxID)
	}

	podInfraContainer := sb.name + "-infra"
	for _, c := range sb.containers.List() {
		if podInfraContainer == c.Name() {
			podNamespace := ""
			netnsPath, err := c.NetNsPath()
			if err != nil {
				return nil, err
			}
			if err := s.netPlugin.TearDownPod(netnsPath, podNamespace, sandboxID, podInfraContainer); err != nil {
				return nil, fmt.Errorf("failed to destroy network for container %s in sandbox %s: %v", c.Name(), sandboxID, err)
			}
		}
		cStatus := s.runtime.ContainerStatus(c)
		if cStatus.Status != "stopped" {
			if err := s.runtime.StopContainer(c); err != nil {
				return nil, fmt.Errorf("failed to stop container %s in sandbox %s: %v", c.Name(), sandboxID, err)
			}
		}
	}

	return &pb.StopPodSandboxResponse{}, nil
}

// RemovePodSandbox deletes the sandbox. If there are any running containers in the
// sandbox, they should be force deleted.
func (s *Server) RemovePodSandbox(ctx context.Context, req *pb.RemovePodSandboxRequest) (*pb.RemovePodSandboxResponse, error) {
	sbID := req.GetPodSandboxId()
	if sbID == "" {
		return nil, fmt.Errorf("PodSandboxId should not be empty")
	}

	sandboxID, err := s.podIDIndex.Get(sbID)
	if err != nil {
		return nil, fmt.Errorf("PodSandbox with ID starting with %s not found: %v", sbID, err)
	}

	sb := s.getSandbox(sandboxID)
	if sb == nil {
		return nil, fmt.Errorf("specified sandbox not found: %s", sandboxID)
	}

	podInfraContainerName := sb.name + "-infra"
	var podInfraContainer *oci.Container

	// Delete all the containers in the sandbox
	for _, c := range sb.containers.List() {
		if err := s.runtime.UpdateStatus(c); err != nil {
			return nil, fmt.Errorf("failed to update container state: %v", err)
		}

		cState := s.runtime.ContainerStatus(c)
		if cState.Status == ContainerStateCreated || cState.Status == ContainerStateRunning {
			if err := s.runtime.StopContainer(c); err != nil {
				return nil, fmt.Errorf("failed to stop container %s: %v", c.Name(), err)
			}
		}

		if err := s.runtime.DeleteContainer(c); err != nil {
			return nil, fmt.Errorf("failed to delete container %s in sandbox %s: %v", c.Name(), sandboxID, err)
		}

		if podInfraContainerName == c.Name() {
			podInfraContainer = c
			continue
		}

		containerDir := filepath.Join(s.runtime.ContainerDir(), c.Name())
		if err := os.RemoveAll(containerDir); err != nil {
			return nil, fmt.Errorf("failed to remove container %s directory: %v", c.Name(), err)
		}

		s.removeContainer(c)
	}

	// Remove the files related to the sandbox
	podSandboxDir := filepath.Join(s.sandboxDir, sandboxID)
	if err := os.RemoveAll(podSandboxDir); err != nil {
		return nil, fmt.Errorf("failed to remove sandbox %s directory: %v", sandboxID, err)
	}
	s.removeContainer(podInfraContainer)

	s.releasePodName(sb.name)
	s.removeSandbox(sandboxID)

	return &pb.RemovePodSandboxResponse{}, nil
}

// PodSandboxStatus returns the Status of the PodSandbox.
func (s *Server) PodSandboxStatus(ctx context.Context, req *pb.PodSandboxStatusRequest) (*pb.PodSandboxStatusResponse, error) {
	sbID := req.GetPodSandboxId()
	if sbID == "" {
		return nil, fmt.Errorf("PodSandboxId should not be empty")
	}

	sandboxID, err := s.podIDIndex.Get(sbID)
	if err != nil {
		return nil, fmt.Errorf("PodSandbox with ID starting with %s not found: %v", sbID, err)
	}

	sb := s.getSandbox(sandboxID)
	if sb == nil {
		return nil, fmt.Errorf("specified sandbox not found: %s", sandboxID)
	}

	podInfraContainerName := sb.name + "-infra"
	podInfraContainer := sb.getContainer(podInfraContainerName)
	if err = s.runtime.UpdateStatus(podInfraContainer); err != nil {
		return nil, err
	}

	cState := s.runtime.ContainerStatus(podInfraContainer)
	created := cState.Created.Unix()

	netNsPath, err := podInfraContainer.NetNsPath()
	if err != nil {
		return nil, err
	}
	podNamespace := ""
	ip, err := s.netPlugin.GetContainerNetworkStatus(netNsPath, podNamespace, sandboxID, podInfraContainerName)
	if err != nil {
		// ignore the error on network status
		ip = ""
	}

	rStatus := pb.PodSandBoxState_NOTREADY
	if cState.Status == ContainerStateRunning {
		rStatus = pb.PodSandBoxState_READY
	}

	return &pb.PodSandboxStatusResponse{
		Status: &pb.PodSandboxStatus{
			Id:        &sbID,
			CreatedAt: int64Ptr(created),
			Linux: &pb.LinuxPodSandboxStatus{
				Namespaces: &pb.Namespace{
					Network: sPtr(netNsPath),
				},
			},
			Network: &pb.PodSandboxNetworkStatus{Ip: &ip},
			State:   &rStatus,
		},
	}, nil
}

// ListPodSandbox returns a list of SandBoxes.
func (s *Server) ListPodSandbox(context.Context, *pb.ListPodSandboxRequest) (*pb.ListPodSandboxResponse, error) {
	var pods []*pb.PodSandbox
	for _, sb := range s.state.sandboxes {
		podInfraContainerName := sb.name + "-infra"
		podInfraContainer := sb.getContainer(podInfraContainerName)
		if podInfraContainer == nil {
			// this can't really happen, but if it does because of a bug
			// it's better not to panic
			continue
		}
		if err := s.runtime.UpdateStatus(podInfraContainer); err != nil {
			return nil, err
		}
		cState := s.runtime.ContainerStatus(podInfraContainer)
		created := cState.Created.Unix()
		rStatus := pb.PodSandBoxState_NOTREADY
		if cState.Status == ContainerStateRunning {
			rStatus = pb.PodSandBoxState_READY
		}

		pod := &pb.PodSandbox{
			Id:        &sb.id,
			CreatedAt: int64Ptr(created),
			State:     &rStatus,
		}

		pods = append(pods, pod)
	}

	return &pb.ListPodSandboxResponse{
		Items: pods,
	}, nil
}
<|endoftext|>


<commit_before>/**
 * Functionality to persist information about feeds that
 * have been subscribed to and any data we might want to
 * keep about things like the number of items retrieved.
 */

package main

import (
	"database/sql"
	rss "github.com/jteeuwen/go-pkg-rss"
	"github.com/jteeuwen/go-pkg-xmlx"
	_ "github.com/mattn/go-sqlite3"
	"time"
)

// For information about RSS items, reference the go-pkg-rss repository
// https://github.com/jteeuwen/go-pkg-rss/blob/066500420ea3ad0509d656c4b0fcbd351223d48e/item.go
type FeedItem struct {
	rss.Item
	Id int
}

// Stores information about a feed in the context of the reader service
type FeedStats struct {
	ItemsReceived int       // The number of items received from the feed
	RequestCount  int       // The number of times users have requested the feed
	LastPublished time.Time // The date of the feed's last known publication
	SubscribedOn  time.Time // The date that the reader subscribed to the feed
}

/***********************************************************
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * * * * * * * * * FeedInfo management * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
***********************************************************/

/**
 * Persist information about a feed to the storage medium.
 * @param {*db.DB} db - The database connection to use
 * @param {FeedInfo} feed - Information describing the new feed to save
 */
func SaveNewFeed(db *db.DB, feed FeedInfo) {

}

/**
 * Get a collection of all the feeds subscribed to.
 * @param {*db.DB} db - The database connection to use
 */
func AllFeeds(db *db.DB) []FeedInfo {

}

/**
 * Get the basic information about a persisted feed from its URL.
 * @param {*db.DB} db - The database connection to use
 * @param {string} url - The URL to search for
 */
func GetFeedByUrl(db *db.DB, url string) FeedInfo {

}

/**
 * Get the basic information about a persisted feed from its ID.
 * @param {*db.DB} db - The database connection to use
 * @param {int} id - The identifier of the feed
 */
func GetFeedById(db *db.DB, id int) FeedInfo {

}

/**
 * Update the info about a feed.
 * @param {*db.DB} db - The database connection to use
 * @param {int} id - The ID of the feed to be updated
 * @param {FeedInfo} feed - The new feed data
 */
func UpdateFeed(db *db.DB, id int, feed FeedInfo) {

}

/**
 * Delete a feed by referencing its URL.
 * @param {*db.DB} db - The database connection to use
 * @param {string} url - The URL of the feed
 */
func DeleteFeedByUrl(db *db.DB, url string) {

}

/**
 * Delete a feed by referencing its ID.
 * @param {*db.DB} db - The database connection to use
 * @param {int} id - The identifier for the feed
 */
func DeleteFeedById(db *db.DB, id int) {

}

/***********************************************************
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * * * * * * * * FeedStats  management * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
***********************************************************/

/**
 * Get statistics about a feed by the feed's URL.
 * @param {*db.DB} db - The database connection to use
 * @param {string} url - The URL of the feed to get stats for
 */
func GetStatsByFeedUrl(db *db.DB, url string) FeedStats {

}

/**
 * Get statistics about a feed by referencing the feed's ID.
 * @param {*db.DB} db - The database connection to use
 * @param {int} id - The identifier of the feed to get stats for
 */
func GetStatsByFeedId(db *db.DB, id int) FeedStats {

}

/**
 * Update the statistics about a feed based on the corresponding feed's ID.
 * @param {*db.DB} db - The database connection to use
 * @param {int} feedId - The identifier of the FeedInfo to reference
 * @param {FeedStats} stats - The new set of statistics to store
 */
func UpdateStatsByFeedId(db *db.DB, feedId int, stats Feedstats) {

}

/***********************************************************
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * * * * * * * * RSS Items  management * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
***********************************************************/

/**
 * Store information about an item in a channel from a particular feed
 * @param {*db.DB} db - the database connection to use
 * @param {int} feedId - The identifier for the feed the item comes from
 * @param {*rss.Channel} channel - The channel containing the item
 * @param {*rss.Item} item - The item to store the content of
 */
func SaveNewItem(db *db.DB, feedId int, channel *rss.Channel, item *rss.Item) {

}

/**
 * Get the items stored for a particular feed in reference to its URL.
 * @param {*db.DB} db - the database connection to use
 * @param {string} url - The URL of the feed to get items from
 */
func GetItemsByFeedId(db *db.DB, url string) []FeedItem {

}

/**
 * Get the items stored for a particular feed in reference to its Id.
 * @param {*db.DB} db - The database connection to use
 * @param {int} feedId - The identifier of the feed to get items from
 */
func GetItemsByFeedId(db *db.DB, feedId int) []FeedItem {

}

/**
 * Delete a particular item.
 * @param {*db.DB} db - The database connection to use
 * @param {int} id - The identifier of the item to delete
 */
func DeleteItem(db *db.DB, id int) {

}
<commit_msg>Working on a nice abstraction over the standard database interaction flow<commit_after>/**
 * Functionality to persist information about feeds that
 * have been subscribed to and any data we might want to
 * keep about things like the number of items retrieved.
 */

package main

import (
	"database/sql"
	rss "github.com/jteeuwen/go-pkg-rss"
	"github.com/jteeuwen/go-pkg-xmlx"
	_ "github.com/mattn/go-sqlite3"
	"time"
)

// For information about RSS items, reference the go-pkg-rss repository
// https://github.com/jteeuwen/go-pkg-rss/blob/066500420ea3ad0509d656c4b0fcbd351223d48e/item.go
type FeedItem struct {
	rss.Item
	Id int
}

// Stores information about a feed in the context of the reader service
type FeedStats struct {
	ItemsReceived int       // The number of items received from the feed
	RequestCount  int       // The number of times users have requested the feed
	LastPublished time.Time // The date of the feed's last known publication
	SubscribedOn  time.Time // The date that the reader subscribed to the feed
}

/***********************************************************
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * * * * * * * * * * Helper  functions * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
***********************************************************/

const createFeedsTable = `create table if not exists feeds(
    id integer primary key,
    url varchar(255),
    type varchar(8),
    charset varchar(64)
);`

const createStatisticsTable = `create table if not exists statistics(
    id integer primary key,
    feed_id integer,
    items_received integer,
    request_count integer,
    subscribed_on date,
    last_published date,
    foreign key(feed_id) references feeds(id)
);`

const createLinksTable = `create table if not exists links(
    id integer primary key,
    item_id integer,
    href varchar(255),
    rel varchar(64),
    type varchar(64),
    href_language varchar(128),
    foreign key(item_id) references items(id)
);`

const createItemsTable = `create table if not exists items(
    id integer primary key,
    feed_id integer,
    title varchar(255),
    authors varchar(255),
    description text,
    content text,
    comments text,
    published date,
    updated date,
    foreign key(feed_id) references feeds(id)
);`

const tableInitializers = []string{
	createFeedsTable,
	createStatisticstable,
	createItemsTable,
	createLinksTable,
}

/**
 * Types and methods for handling the mechanics of executing statements/queries
 * that manages the propagation of errors as well as internal state such as
 * transactions (Tx) and statements (Stmt).
 */

type transaction struct {
	Db *sql.DB
}

type txwrapper struct {
	Tx    *sql.Tx
	Query string
	Err   error
}

type stmtwrapper struct {
	Tx      *sql.Tx
	Stmt    *sql.Stmt
	Args    []interface{}
	IsQuery bool
	Err     error
}

/**
 * INTERAL (transaction not public)
 * Opens a transaction (*sql.Tx) up in preparation for the creation of a
 * statement with a given query.
 * @param {string} query - The SQL query or statement to run
 */
func (t transaction) Prepare(query string) txwrapper {
	tx, err := t.Db.Begin()
	if err != nil {
		tx.Close()
		return txwrapper{nil, "", err}
	}
	return txwrapper{tx, query, nil}
}

/**
 * INTERNAL (txwrapper not public)
 * Prepares a query to be run into a *sql.Stmt.
 * @param {...interface{}} args - The arguments to pass as parameters to the query
 */
func (t txwrapper) Query(args ...interface{}) stmtwrapper {
	if t.Err != nil {
		emptyArray := make([]interface{}, 0)
		return stmtwrapper{nil, nil, emptyArray, true, t.Err}
	}
	stmt, err := t.Tx.Prepare(t.Query)
	if err != nil {
		t.Tx.Close()
		emptyArray := make([]interface{}, 0)
		return stmtwrapper{nil, nil, emptyArray, true, err}
	}
	return stmtwrapper{t.Tx, stmt, args, true, nil}
}

/**
 * INTERNAL (txwrapper not pubic)
 * Prepares a statement to be run into a *sql.Stmt.
 * @param {...interface{}} args - The arguments to pass as parameters to the statement
 */
func (t txwrapper) Exec(args ...interface{}) stmtwrapper {
	s := t.Query(args...)
	s.IsQuery = false
	return s
}

/**
 * INTERNAL (stmtwrapper not public)
 * Executes the created statement or query and commits the transaction
 * if the statement is successful before clearing the transaction and statement.
 * @return The Rows pointer and error (or nil) produced by the Query/Exec call
 */
func (s stmtwrapper) Run() (*Rows, error) {
	if s.Err != nil {
		if s.Tx != nil {
			s.Tx.Close()
		}
		if s.Stmt != nil {
			s.Stmt.Close()
		}
		return nil, s.Err
	}
	var rows *Rows
	var err error
	if s.IsQuery {
		rows, err = s.Stmt.Query(s.Args...)
	} else {
		rows, err = s.Stmt.Exec(s.Args...)
		s.Tx.Commit()
	}
	s.Stmt.Close()
	s.Tx.Close()
	return rows, err
}

/**
 * Create a database connection to SQLite3 and initialize (if not already)
 * the tables used to store information about RSS feeds being followed etc.
 * @param {string} dbFileName - The name of the file to keep the database info in
 */
func InitDBConnection(dbFileName string) (*sql.DB, error) {
	db, err := sql.Open("sqlite3", dbFileName)
	if err != nil {
		return nil, err
	}
	for _, initializer := range tableInitializers {
		_, err = db.Exec(initializer)
		if err != nil {
			break
		}
	}
	return db, err
}

/***********************************************************
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * * * * * * * * * FeedInfo management * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
***********************************************************/

/**
 * Persist information about a feed to the storage medium.
 * @param {*sql.DB} db - The database connection to use
 * @param {FeedInfo} feed - Information describing the new feed to save
 */
func SaveNewFeed(db *sql.DB, feed FeedInfo) error {
	_, err := transaction{db}.
		Prepare("insert into feeds(url, type, charset) values(?,?,?)").
		Exec(feed.URL, feed.Type, feed.Charset).
		Run()
	return err
}

/**
 * Get a collection of all the feeds subscribed to.
 * @param {*sql.DB} db - The database connection to use
 */
//func AllFeeds(db *sql.DB) []FeedInfo {
//}

/**
 * Get the basic information about a persisted feed from its URL.
 * @param {*sql.DB} db - The database connection to use
 * @param {string} url - The URL to search for
 */
//func GetFeedByUrl(db *sql.DB, url string) FeedInfo {
//}

/**
 * Get the basic information about a persisted feed from its ID.
 * @param {*sql.DB} db - The database connection to use
 * @param {int} id - The identifier of the feed
 */
//func GetFeedById(db *sql.DB, id int) FeedInfo {
//}

/**
 * Update the info about a feed.
 * @param {*sql.DB} db - The database connection to use
 * @param {int} id - The ID of the feed to be updated
 * @param {FeedInfo} feed - The new feed data
 */
//func UpdateFeed(db *sql.DB, id int, feed FeedInfo) {
//}

/**
 * Delete a feed by referencing its URL.
 * @param {*sql.DB} db - The database connection to use
 * @param {string} url - The URL of the feed
 */
//func DeleteFeedByUrl(db *sql.DB, url string) {
//}

/**
 * Delete a feed by referencing its ID.
 * @param {*sql.DB} db - The database connection to use
 * @param {int} id - The identifier for the feed
 */
//func DeleteFeedById(db *sql.DB, id int) {
//}

/***********************************************************
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * * * * * * * * FeedStats  management * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
***********************************************************/

/**
 * Get statistics about a feed by the feed's URL.
 * @param {*sql.DB} db - The database connection to use
 * @param {string} url - The URL of the feed to get stats for
 */
//func GetStatsByFeedUrl(db *sql.DB, url string) FeedStats {
//}

/**
 * Get statistics about a feed by referencing the feed's ID.
 * @param {*sql.DB} db - The database connection to use
 * @param {int} id - The identifier of the feed to get stats for
 */
//func GetStatsByFeedId(db *sql.DB, id int) FeedStats {
//}

/**
 * Update the statistics about a feed based on the corresponding feed's ID.
 * @param {*sql.DB} db - The database connection to use
 * @param {int} feedId - The identifier of the FeedInfo to reference
 * @param {FeedStats} stats - The new set of statistics to store
 */
//func UpdateStatsByFeedId(db *sql.DB, feedId int, stats Feedstats) {
//}

/***********************************************************
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * * * * * * * * RSS Items  management * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
***********************************************************/

/**
 * Store information about an item in a channel from a particular feed
 * @param {*sql.DB} db - the database connection to use
 * @param {int} feedId - The identifier for the feed the item comes from
 * @param {*rss.Channel} channel - The channel containing the item
 * @param {*rss.Item} item - The item to store the content of
 */
//func SaveNewItem(db *sql.DB, feedId int, channel *rss.Channel, item *rss.Item) {
//}

/**
 * Get the items stored for a particular feed in reference to its URL.
 * @param {*sql.DB} db - the database connection to use
 * @param {string} url - The URL of the feed to get items from
 */
//func GetItemsByFeedId(db *sql.DB, url string) []FeedItem {
//}

/**
 * Get the items stored for a particular feed in reference to its Id.
 * @param {*sql.DB} db - The database connection to use
 * @param {int} feedId - The identifier of the feed to get items from
 */
//func GetItemsByFeedId(db *sql.DB, feedId int) []FeedItem {
//}

/**
 * Delete a particular item.
 * @param {*sql.DB} db - The database connection to use
 * @param {int} id - The identifier of the item to delete
 */
//func DeleteItem(db *sql.DB, id int) {
//}
<|endoftext|>


<commit_before>package endpoint

import (
	"encoding/json"
	"io/ioutil"
	"net/http"
)

const (
	// GET request
	GET = "GET"

	// POST request
	POST = "POST"
)

func get(ctx Context, h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.Method == GET {
			h.ServeHTTP(w, r)
		} else {
			w.WriteHeader(http.StatusMethodNotAllowed)
		}
	})
}

func post(ctx Context, h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.Method == POST {
			var data map[string]interface{}
			body := json.NewDecoder(r.Body)
			err := body.Decode(&data)
			if err != nil {
				w.WriteHeader(http.StatusBadRequest)
				return
			}
			ctx["data"] = data
		} else {
			w.WriteHeader(http.StatusMethodNotAllowed)
		}
	})
}
<commit_msg>removed dependency<commit_after>package endpoint

import (
	"encoding/json"
	"net/http"
)

const (
	// GET request
	GET = "GET"

	// POST request
	POST = "POST"
)

func get(ctx Context, h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.Method == GET {
			h.ServeHTTP(w, r)
		} else {
			w.WriteHeader(http.StatusMethodNotAllowed)
		}
	})
}

func post(ctx Context, h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.Method == POST {
			var data map[string]interface{}
			body := json.NewDecoder(r.Body)
			err := body.Decode(&data)
			if err != nil {
				w.WriteHeader(http.StatusBadRequest)
				return
			}
			ctx["data"] = data
		} else {
			w.WriteHeader(http.StatusMethodNotAllowed)
		}
	})
}
<|endoftext|>


<commit_before>package cron

import (
	"context"
	"fmt"
	"testing"
	"time"
)

func TestCron(t *testing.T) {
	s1 := "1/2 * * * * *"
	s2 := "1/2 3 * * * *"
	s3 := "1/5 * * * * *"
	s4 := "8/12 * * * * *"
	ctx, cancel := context.WithCancel(context.Background())
	cron := New(ctx, cancel)

	go func() {
		for err := range cron.Err {
			fmt.Printf("ERROR: %s\n", err)
		}
	}()

	fmt.Printf("%s\n", time.Now())
	fmt.Println("add s1")
	cron.AddFunc(ctx, "s1", s1, func() {
		fmt.Printf("s1 %s: %s\n", s1, time.Now())
	})
	go cron.Start(ctx)
	fmt.Println("add s2")
	cron.AddFunc(ctx, "s2", s2, func() {
		fmt.Printf("s2 %s: %s\n", s2, time.Now())
	})
	fmt.Println("add s3")
	cron.AddFunc(ctx, "s3", s3, func() {
		fmt.Printf("s3 %s: %s\n", s3, time.Now())
	})
	fmt.Println("add s4, but name is s3")
	cron.AddFunc(ctx, "s3", s4, func() {
		fmt.Printf("s4 %s: %s\n", s4, time.Now())
	})

	go func() {
		time.Sleep(5 * time.Second)
		fmt.Println("delete s1")
		cron.Delete("s1")
	}()
	time.Sleep(10 * time.Second)
	fmt.Println("stop cron")
	cron.Stop()
	fmt.Println("wait stop!")
	time.Sleep(1 * time.Second)
}
<commit_msg>change cron to cr<commit_after>package cron

import (
	"context"
	"fmt"
	"testing"
	"time"
)

func TestCron(t *testing.T) {
	s1 := "1/2 * * * * *"
	s2 := "1/2 3 * * * *"
	s3 := "1/5 * * * * *"
	s4 := "8/12 * * * * *"
	ctx, cancel := context.WithCancel(context.Background())
	cr := New(ctx, cancel)

	go func() {
		for err := range cr.Err {
			fmt.Printf("ERROR: %s\n", err)
		}
	}()

	fmt.Printf("%s\n", time.Now())
	fmt.Println("add s1")
	cr.AddFunc(ctx, "s1", s1, func() {
		fmt.Printf("s1 %s: %s\n", s1, time.Now())
	})
	go cr.Start(ctx)
	fmt.Println("add s2")
	cr.AddFunc(ctx, "s2", s2, func() {
		fmt.Printf("s2 %s: %s\n", s2, time.Now())
	})
	fmt.Println("add s3")
	cr.AddFunc(ctx, "s3", s3, func() {
		fmt.Printf("s3 %s: %s\n", s3, time.Now())
	})
	fmt.Println("add s4, but name is s3")
	cr.AddFunc(ctx, "s3", s4, func() {
		fmt.Printf("s4 %s: %s\n", s4, time.Now())
	})

	go func() {
		time.Sleep(5 * time.Second)
		fmt.Println("delete s1")
		cr.Delete("s1")
	}()
	time.Sleep(10 * time.Second)
	fmt.Println("stop cron")
	cr.Stop()
	fmt.Println("wait stop!")
	time.Sleep(1 * time.Second)
}
<|endoftext|>