<commit_before>package tdb

import (
	"fmt"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"sync"
	"time"

	"github.com/Knetic/govaluate"
	"github.com/getlantern/errors"
	"github.com/getlantern/golog"
	"github.com/getlantern/tdb/sql"
	"github.com/getlantern/vtime"
	"github.com/tecbot/gorocksdb"
)

var (
	log = golog.LoggerFor("tdb")
)

type TableStats struct {
	FilteredPoints  int64
	InsertedPoints  int64
	DroppedPoints   int64
	HotKeys         int64
	ArchivedBuckets int64
	ExpiredKeys     int64
}

type table struct {
	sql.Query
	db              *DB
	name            string
	sqlString       string
	log             golog.Logger
	batchSize       int64
	clock           *vtime.Clock
	archiveByKey    *gorocksdb.DB
	hotPeriod       time.Duration
	retentionPeriod time.Duration
	partitions      []*partition
	toArchive       chan *archiveRequest
	where           *govaluate.EvaluableExpression
	whereMutex      sync.RWMutex
	stats           TableStats
	statsMutex      sync.RWMutex
}

type DBOpts struct {
	SchemaFile string
	Dir        string
	BatchSize  int64
}

type DB struct {
	opts        *DBOpts
	streams     map[string][]*table
	tables      map[string]*table
	tablesMutex sync.RWMutex
}

type view struct {
	To   string
	Dims map[string]bool
}

func NewDB(opts *DBOpts) (*DB, error) {
	var err error
	db := &DB{opts: opts, tables: make(map[string]*table), streams: make(map[string][]*table)}
	if opts.SchemaFile != "" {
		err = db.pollForSchema(opts.SchemaFile)
	}
	return db, err
}

func (db *DB) CreateTable(name string, hotPeriod time.Duration, retentionPeriod time.Duration, sqlString string) error {
	if hotPeriod <= 0 {
		return errors.New("Please specify a positive hot period")
	}
	if retentionPeriod <= 0 {
		return errors.New("Please specify a positive retention period")
	}
	q, err := sql.Parse(sqlString)
	if err != nil {
		return err
	}

	return db.doCreateTable(name, hotPeriod, retentionPeriod, sqlString, q)
}

func (db *DB) doCreateTable(name string, hotPeriod time.Duration, retentionPeriod time.Duration, sqlString string, q *sql.Query) error {
	name = strings.ToLower(name)

	t := &table{
		Query:           *q,
		db:              db,
		name:            name,
		sqlString:       sqlString,
		log:             golog.LoggerFor("tdb." + name),
		batchSize:       db.opts.BatchSize,
		clock:           vtime.NewClock(time.Time{}),
		hotPeriod:       hotPeriod,
		retentionPeriod: retentionPeriod,
		toArchive:       make(chan *archiveRequest, db.opts.BatchSize*100),
	}

	err := t.applyWhere(q.Where)
	if err != nil {
		return err
	}

	numCPU := runtime.NumCPU()
	t.partitions = make([]*partition, 0, numCPU)
	for i := 0; i < numCPU; i++ {
		p := &partition{
			t:            t,
			archiveDelay: time.Duration(i) * t.archivePeriod() / time.Duration(numCPU),
			inserts:      make(chan *insert, 100000/numCPU),
			tail:         make(map[string]*bucket),
		}
		t.partitions = append(t.partitions, p)
	}

	db.tablesMutex.Lock()
	defer db.tablesMutex.Unlock()

	if db.tables[name] != nil {
		return fmt.Errorf("Table %v already exists", name)
	}

	err = os.MkdirAll(db.opts.Dir, 0755)
	if err != nil && !os.IsExist(err) {
		return fmt.Errorf("Unable to create folder for rocksdb database: %v", err)
	}
	t.archiveByKey, err = t.createDatabase(db.opts.Dir, "bykey")
	if err != nil {
		return fmt.Errorf("Unable to create rocksdb database: %v", err)
	}
	db.tables[name] = t

	for i := 0; i < numCPU; i++ {
		go t.partitions[i].processInserts()
	}

	go t.archive()
	go t.retain()

	db.streams[q.From] = append(db.streams[q.From], t)

	return nil
}

func (t *table) applyWhere(where string) error {
	var e *govaluate.EvaluableExpression
	var err error
	if where != "" {
		e, err = govaluate.NewEvaluableExpression(where)
		if err != nil {
			return fmt.Errorf("Unable to parse where: %v", err)
		}
	}
	t.whereMutex.Lock()
	t.Where = where
	t.where = e
	t.whereMutex.Unlock()
	return nil
}

func (db *DB) TableStats(table string) TableStats {
	t := db.getTable(table)
	if t == nil {
		return TableStats{}
	}
	t.statsMutex.RLock()
	defer t.statsMutex.RUnlock()
	return t.stats
}

func (db *DB) Now(table string) time.Time {
	t := db.getTable(table)
	if t == nil {
		return time.Time{}
	}
	return t.clock.Now()
}

func (db *DB) getTable(table string) *table {
	db.tablesMutex.RLock()
	t := db.tables[strings.ToLower(table)]
	db.tablesMutex.RUnlock()
	return t
}

func (t *table) createDatabase(dir string, suffix string) (*gorocksdb.DB, error) {
	opts := gorocksdb.NewDefaultOptions()
	bbtopts := gorocksdb.NewDefaultBlockBasedTableOptions()
	// TODO: make this tunable or auto-adjust based on table resolution or
	// something
	bbtopts.SetBlockCache(gorocksdb.NewLRUCache(256 * 1024 * 1024))
	filter := gorocksdb.NewBloomFilter(10)
	bbtopts.SetFilterPolicy(filter)
	opts.SetBlockBasedTableFactory(bbtopts)
	opts.SetCreateIfMissing(true)
	opts.SetMergeOperator(t)
	return gorocksdb.OpenDb(opts, filepath.Join(dir, t.name+"_"+suffix))
}

func roundTime(ts time.Time, resolution time.Duration) time.Time {
	rounded := ts.Round(resolution)
	if rounded.After(ts) {
		rounded = rounded.Add(-1 * resolution)
	}
	return rounded
}
<commit_msg>Allowing getting stats for all tables<commit_after>package tdb

import (
	"fmt"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"sync"
	"time"

	"github.com/Knetic/govaluate"
	"github.com/getlantern/errors"
	"github.com/getlantern/golog"
	"github.com/getlantern/tdb/sql"
	"github.com/getlantern/vtime"
	"github.com/tecbot/gorocksdb"
)

var (
	log = golog.LoggerFor("tdb")
)

type TableStats struct {
	FilteredPoints  int64
	InsertedPoints  int64
	DroppedPoints   int64
	HotKeys         int64
	ArchivedBuckets int64
	ExpiredKeys     int64
}

type table struct {
	sql.Query
	db              *DB
	name            string
	sqlString       string
	log             golog.Logger
	batchSize       int64
	clock           *vtime.Clock
	archiveByKey    *gorocksdb.DB
	hotPeriod       time.Duration
	retentionPeriod time.Duration
	partitions      []*partition
	toArchive       chan *archiveRequest
	where           *govaluate.EvaluableExpression
	whereMutex      sync.RWMutex
	stats           TableStats
	statsMutex      sync.RWMutex
}

type DBOpts struct {
	SchemaFile string
	Dir        string
	BatchSize  int64
}

type DB struct {
	opts        *DBOpts
	streams     map[string][]*table
	tables      map[string]*table
	tablesMutex sync.RWMutex
}

type view struct {
	To   string
	Dims map[string]bool
}

func NewDB(opts *DBOpts) (*DB, error) {
	var err error
	db := &DB{opts: opts, tables: make(map[string]*table), streams: make(map[string][]*table)}
	if opts.SchemaFile != "" {
		err = db.pollForSchema(opts.SchemaFile)
	}
	return db, err
}

func (db *DB) CreateTable(name string, hotPeriod time.Duration, retentionPeriod time.Duration, sqlString string) error {
	if hotPeriod <= 0 {
		return errors.New("Please specify a positive hot period")
	}
	if retentionPeriod <= 0 {
		return errors.New("Please specify a positive retention period")
	}
	q, err := sql.Parse(sqlString)
	if err != nil {
		return err
	}

	return db.doCreateTable(name, hotPeriod, retentionPeriod, sqlString, q)
}

func (db *DB) doCreateTable(name string, hotPeriod time.Duration, retentionPeriod time.Duration, sqlString string, q *sql.Query) error {
	name = strings.ToLower(name)

	t := &table{
		Query:           *q,
		db:              db,
		name:            name,
		sqlString:       sqlString,
		log:             golog.LoggerFor("tdb." + name),
		batchSize:       db.opts.BatchSize,
		clock:           vtime.NewClock(time.Time{}),
		hotPeriod:       hotPeriod,
		retentionPeriod: retentionPeriod,
		toArchive:       make(chan *archiveRequest, db.opts.BatchSize*100),
	}

	err := t.applyWhere(q.Where)
	if err != nil {
		return err
	}

	numCPU := runtime.NumCPU()
	t.partitions = make([]*partition, 0, numCPU)
	for i := 0; i < numCPU; i++ {
		p := &partition{
			t:            t,
			archiveDelay: time.Duration(i) * t.archivePeriod() / time.Duration(numCPU),
			inserts:      make(chan *insert, 100000/numCPU),
			tail:         make(map[string]*bucket),
		}
		t.partitions = append(t.partitions, p)
	}

	db.tablesMutex.Lock()
	defer db.tablesMutex.Unlock()

	if db.tables[name] != nil {
		return fmt.Errorf("Table %v already exists", name)
	}

	err = os.MkdirAll(db.opts.Dir, 0755)
	if err != nil && !os.IsExist(err) {
		return fmt.Errorf("Unable to create folder for rocksdb database: %v", err)
	}
	t.archiveByKey, err = t.createDatabase(db.opts.Dir, "bykey")
	if err != nil {
		return fmt.Errorf("Unable to create rocksdb database: %v", err)
	}
	db.tables[name] = t

	for i := 0; i < numCPU; i++ {
		go t.partitions[i].processInserts()
	}

	go t.archive()
	go t.retain()

	db.streams[q.From] = append(db.streams[q.From], t)

	return nil
}

func (t *table) applyWhere(where string) error {
	var e *govaluate.EvaluableExpression
	var err error
	if where != "" {
		e, err = govaluate.NewEvaluableExpression(where)
		if err != nil {
			return fmt.Errorf("Unable to parse where: %v", err)
		}
	}
	t.whereMutex.Lock()
	t.Where = where
	t.where = e
	t.whereMutex.Unlock()
	return nil
}

func (db *DB) TableStats(table string) TableStats {
	t := db.getTable(table)
	if t == nil {
		return TableStats{}
	}
	t.statsMutex.RLock()
	defer t.statsMutex.RUnlock()
	return t.stats
}

func (db *DB) AllTableStats() map[string]TableStats {
	m := make(map[string]TableStats)
	tables := make(map[string]*table, 0)
	db.tablesMutex.RLock()
	for name, t := range db.tables {
		tables[name] = t
	}
	db.tablesMutex.RUnlock()
	for name, t := range tables {
		t.statsMutex.RLock()
		m[name] = t.stats
		t.statsMutex.RUnlock()
	}
	return m
}

func (db *DB) Now(table string) time.Time {
	t := db.getTable(table)
	if t == nil {
		return time.Time{}
	}
	return t.clock.Now()
}

func (db *DB) getTable(table string) *table {
	db.tablesMutex.RLock()
	t := db.tables[strings.ToLower(table)]
	db.tablesMutex.RUnlock()
	return t
}

func (t *table) createDatabase(dir string, suffix string) (*gorocksdb.DB, error) {
	opts := gorocksdb.NewDefaultOptions()
	bbtopts := gorocksdb.NewDefaultBlockBasedTableOptions()
	// TODO: make this tunable or auto-adjust based on table resolution or
	// something
	bbtopts.SetBlockCache(gorocksdb.NewLRUCache(256 * 1024 * 1024))
	filter := gorocksdb.NewBloomFilter(10)
	bbtopts.SetFilterPolicy(filter)
	opts.SetBlockBasedTableFactory(bbtopts)
	opts.SetCreateIfMissing(true)
	opts.SetMergeOperator(t)
	return gorocksdb.OpenDb(opts, filepath.Join(dir, t.name+"_"+suffix))
}

func roundTime(ts time.Time, resolution time.Duration) time.Time {
	rounded := ts.Round(resolution)
	if rounded.After(ts) {
		rounded = rounded.Add(-1 * resolution)
	}
	return rounded
}
<|endoftext|>


<commit_before>package endly

import "fmt"

func transformWithUDF(context *Context, udfName, source string, payload interface{}) (interface{}, error) {
	var state = context.state
	var udf, has = UdfRegistry[udfName]
	if !has {
		return nil, fmt.Errorf("failed to lookup udf: %v for: %v", udfName, source)
	}
	transformed, err := udf(payload, state)
	if err != nil {
		return nil, fmt.Errorf("failed to run udf: %v, %v", udfName, err)
	}
	return transformed, nil
}
<commit_msg>patched transformWithUDF<commit_after>package endly

import (
	"fmt"
	"github.com/viant/toolbox/data"
)

func transformWithUDF(context *Context, udfName, source string, payload interface{}) (interface{}, error) {
	var state = context.state
	var udf, has = UdfRegistry[udfName]
	if !has {
		if candidate, ok := state[udfName]; ok {
			udf, has = candidate.(func(source interface{}, state data.Map) (interface{}, error))
		}
	}
	if !has {
		return nil, fmt.Errorf("failed to lookup udf: %v for: %v", udfName, source)
	}
	transformed, err := udf(payload, state)
	if err != nil {
		return nil, fmt.Errorf("failed to run udf: %v, %v", udfName, err)
	}
	return transformed, nil
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/go-martini/martini"
	"github.com/martini-contrib/render"
	"html/template"
	"log"
	"net/http"
	"os"
	"time"
)

func serveHttp() {
	m := martini.Classic()
	m.Use(render.Renderer())

	// UI Calls
	m.Get("/", homeHandler)
	m.Post("/", testCoordinatesHandler)
	m.NotFound(notFoundHandler)

	if err := http.ListenAndServe(":"+os.Getenv("PORT"), m); err != nil {
		panic(err)
	}
}

func homeHandler(w http.ResponseWriter, req *http.Request) {
	t := template.Must(template.New("home").ParseFiles("views/layout.html", "views/home/index.html"))
	t.ExecuteTemplate(w, "layout", "Home")
}

func notFoundHandler(w http.ResponseWriter, req *http.Request) {
	t := template.Must(template.New("404").ParseFiles("views/layout.html", "views/404.html"))
	t.ExecuteTemplate(w, "layout", "Whoops!")
}

func testCoordinatesHandler(req *http.Request, r render.Render) {
	req.ParseForm()
	x := req.FormValue("point-x")
	y := req.FormValue("point-y")
	polygon := req.FormValue("polygon-wkt")
	contained := testCoordinatePresence(x, y, polygon)

	r.JSON(http.StatusOK,
		map[string]interface{}{
			"contained": contained,
			"timestamp": time.Now().UTC().Unix(),
		})
}
<commit_msg>Remove old log import.<commit_after>package main

import (
	"github.com/go-martini/martini"
	"github.com/martini-contrib/render"
	"html/template"
	"net/http"
	"os"
	"time"
)

func serveHttp() {
	m := martini.Classic()
	m.Use(render.Renderer())

	// UI Calls
	m.Get("/", homeHandler)
	m.Post("/", testCoordinatesHandler)
	m.NotFound(notFoundHandler)

	if err := http.ListenAndServe(":"+os.Getenv("PORT"), m); err != nil {
		panic(err)
	}
}

func homeHandler(w http.ResponseWriter, req *http.Request) {
	t := template.Must(template.New("home").ParseFiles("views/layout.html", "views/home/index.html"))
	t.ExecuteTemplate(w, "layout", "Home")
}

func notFoundHandler(w http.ResponseWriter, req *http.Request) {
	t := template.Must(template.New("404").ParseFiles("views/layout.html", "views/404.html"))
	t.ExecuteTemplate(w, "layout", "Whoops!")
}

func testCoordinatesHandler(req *http.Request, r render.Render) {
	req.ParseForm()
	x := req.FormValue("point-x")
	y := req.FormValue("point-y")
	polygon := req.FormValue("polygon-wkt")
	contained := testCoordinatePresence(x, y, polygon)

	r.JSON(http.StatusOK,
		map[string]interface{}{
			"contained": contained,
			"timestamp": time.Now().UTC().Unix(),
		})
}
<|endoftext|>


<commit_before>// +build windows

func bold(str string) string {
    return str
}

func terminalWidth() (int, error) {
    80, nil
}
<commit_msg>Win fix<commit_after>// +build windows

package pb

func bold(str string) string {
    return str
}

func terminalWidth() (int, error) {
    80, nil
}
<|endoftext|>


<commit_before>package tests

import (
	"koding/db/mongodb/modelhelper"
	"socialapi/config"
	"socialapi/models"
	"socialapi/request"
	"socialapi/rest"
	"socialapi/workers/integration/webhook"
	"socialapi/workers/integration/webhook/api"
	"socialapi/workers/integration/webhook/services"
	"testing"

	"github.com/koding/runner"
	. "github.com/smartystreets/goconvey/convey"
)

func newPrepareRequest(email string) *services.ServiceInput {
	return &services.ServiceInput{
		"email":     email,
		"eventName": "emailOpen",
		"dataFields": map[string]interface{}{
			"templateId": float64(15120),
			"device":     "Gmail",
			"createdAt":  "2015-04-24 21:35:02 +00:00",
			"campaignId": float64(5654),
			"userAgent":  "Mozilla/5.0 (Windows NT 5.1; rv:11.0) Gecko Firefox/11.0 (via ggpht.com GoogleImageProxy)",
			"ip":         "66.249.93.139",
		},
	}
}

func newPushRequest(channelId int64, groupName string) *api.WebhookRequest {
	wr := &api.WebhookRequest{
		GroupName: groupName,
	}
	wr.Body = "hey"
	wr.ChannelId = channelId

	return wr
}

func newBotChannelRequest(nick, groupName string) *api.BotChannelRequest {
	return &api.BotChannelRequest{
		GroupName: groupName,
		Username:  nick,
	}
}

func TestWebhook(t *testing.T) {
	r := runner.New("test webhook")
	if err := r.Init(); err != nil {
		t.Fatalf("couldnt start bongo %s", err.Error())
	}
	defer r.Close()

	appConfig := config.MustRead(r.Conf.Path)
	modelhelper.Initialize(appConfig.Mongo)
	defer modelhelper.Close()

	channelIntegration := webhook.CreateTestChannelIntegration(t)

	Convey("While sending a message to push endpoint", t, func() {

		account, err := models.CreateAccountInBothDbsWithNick("sinan")
		So(err, ShouldBeNil)

		channel := models.CreateTypedGroupedChannelWithTest(account.Id, models.Channel_TYPE_TOPIC, channelIntegration.GroupName)
		_, err = channel.AddParticipant(account.Id)
		So(err, ShouldBeNil)

		err = rest.MakePushRequest(newPushRequest(channel.Id, channelIntegration.GroupName), channelIntegration.Token)
		So(err, ShouldBeNil)

		ses, err := models.FetchOrCreateSession(account.Nick)
		So(err, ShouldBeNil)
		So(ses, ShouldNotBeNil)

		resp, err := rest.GetHistory(channel.Id,
			&request.Query{
				AccountId: account.Id,
			},
			ses.ClientId,
		)

		So(err, ShouldBeNil)
		So(len(resp.MessageList), ShouldEqual, 1)
	})

	Convey("While fetching participant bot channel", t, func() {
		account, err := models.CreateAccountInBothDbsWithNick("sinan")
		So(err, ShouldBeNil)
		channel := models.CreateTypedGroupedChannelWithTest(account.Id, models.Channel_TYPE_GROUP, models.RandomName())
		_, err = channel.AddParticipant(account.Id)
		So(err, ShouldBeNil)

		channelId, err := rest.MakeBotChannelRequest(newBotChannelRequest(account.Nick, channel.GroupName))

		So(err, ShouldBeNil)
		So(channelId, ShouldNotEqual, 0)
	})

	Convey("While pushing a message to prepare endpoint", t, func() {
		_, err := models.CreateAccountInBothDbsWithNick("ctf")
		So(err, ShouldBeNil)

		err = rest.MakePrepareRequest(newPrepareRequest("xxx@koding.com"), channelIntegration.Token)
		So(err, ShouldNotBeNil)

		err = rest.MakePrepareRequest(newPrepareRequest("ctf@koding.com"), channelIntegration.Token)
		So(err, ShouldBeNil)
	})

}
<commit_msg>webhook: create iterable integration for tests<commit_after>package tests

import (
	"koding/db/mongodb/modelhelper"
	"socialapi/config"
	"socialapi/models"
	"socialapi/request"
	"socialapi/rest"
	"socialapi/workers/integration/webhook"
	"socialapi/workers/integration/webhook/api"
	"socialapi/workers/integration/webhook/services"
	"testing"

	"github.com/koding/runner"
	. "github.com/smartystreets/goconvey/convey"
)

func newPrepareRequest(email string) *services.ServiceInput {
	return &services.ServiceInput{
		"email":     email,
		"eventName": "emailOpen",
		"dataFields": map[string]interface{}{
			"templateId": float64(15120),
			"device":     "Gmail",
			"createdAt":  "2015-04-24 21:35:02 +00:00",
			"campaignId": float64(5654),
			"userAgent":  "Mozilla/5.0 (Windows NT 5.1; rv:11.0) Gecko Firefox/11.0 (via ggpht.com GoogleImageProxy)",
			"ip":         "66.249.93.139",
		},
	}
}

func newPushRequest(channelId int64, groupName string) *api.WebhookRequest {
	wr := &api.WebhookRequest{
		GroupName: groupName,
	}
	wr.Body = "hey"
	wr.ChannelId = channelId

	return wr
}

func newBotChannelRequest(nick, groupName string) *api.BotChannelRequest {
	return &api.BotChannelRequest{
		GroupName: groupName,
		Username:  nick,
	}
}

func TestWebhook(t *testing.T) {
	r := runner.New("test webhook")
	if err := r.Init(); err != nil {
		t.Fatalf("couldnt start bongo %s", err.Error())
	}
	defer r.Close()

	appConfig := config.MustRead(r.Conf.Path)
	modelhelper.Initialize(appConfig.Mongo)
	defer modelhelper.Close()

	channelIntegration := webhook.CreateTestChannelIntegration(t)

	webhook.CreateIterableIntegration(t)

	Convey("While sending a message to push endpoint", t, func() {

		account, err := models.CreateAccountInBothDbsWithNick("sinan")
		So(err, ShouldBeNil)

		channel := models.CreateTypedGroupedChannelWithTest(account.Id, models.Channel_TYPE_TOPIC, channelIntegration.GroupName)
		_, err = channel.AddParticipant(account.Id)
		So(err, ShouldBeNil)

		err = rest.MakePushRequest(newPushRequest(channel.Id, channelIntegration.GroupName), channelIntegration.Token)
		So(err, ShouldBeNil)

		ses, err := models.FetchOrCreateSession(account.Nick)
		So(err, ShouldBeNil)
		So(ses, ShouldNotBeNil)

		resp, err := rest.GetHistory(channel.Id,
			&request.Query{
				AccountId: account.Id,
			},
			ses.ClientId,
		)

		So(err, ShouldBeNil)
		So(len(resp.MessageList), ShouldEqual, 1)
	})

	SkipConvey("While fetching participant bot channel", t, func() {
		account, err := models.CreateAccountInBothDbsWithNick("sinan")
		So(err, ShouldBeNil)
		channel := models.CreateTypedGroupedChannelWithTest(account.Id, models.Channel_TYPE_GROUP, models.RandomName())
		_, err = channel.AddParticipant(account.Id)
		So(err, ShouldBeNil)

		channelId, err := rest.MakeBotChannelRequest(newBotChannelRequest(account.Nick, channel.GroupName))

		So(err, ShouldBeNil)
		So(channelId, ShouldNotEqual, 0)
	})

	Convey("While pushing a message to prepare endpoint", t, func() {
		_, err := models.CreateAccountInBothDbsWithNick("ctf")
		So(err, ShouldBeNil)

		err = rest.MakePrepareRequest(newPrepareRequest("xxx@koding.com"), channelIntegration.Token)
		So(err, ShouldNotBeNil)

		err = rest.MakePrepareRequest(newPrepareRequest("ctf@koding.com"), channelIntegration.Token)
		So(err, ShouldBeNil)
	})

}
<|endoftext|>


<commit_before>package api

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

	"github.com/af83/edwig/core"
	"github.com/af83/edwig/logger"
)

type PartnerController struct {
	referential *core.Referential
}

func NewPartnerController(referential *core.Referential) *Controller {
	return &Controller{
		restfulRessource: &PartnerController{
			referential: referential,
		},
	}
}

func (controller *PartnerController) Index(response http.ResponseWriter) {
	logger.Log.Debugf("Partners Index")

	jsonBytes, _ := json.Marshal(controller.referential.Partners().FindAll())
	response.Write(jsonBytes)
}

func (controller *PartnerController) Show(response http.ResponseWriter, identifier string) {
	partner := controller.referential.Partners().Find(core.PartnerId(identifier))
	if partner == nil {
		http.Error(response, fmt.Sprintf("Partner not found: %s", identifier), 500)
		return
	}
	logger.Log.Debugf("Get partner %s", identifier)

	jsonBytes, _ := partner.MarshalJSON()
	response.Write(jsonBytes)
}

func (controller *PartnerController) Delete(response http.ResponseWriter, identifier string) {
	partner := controller.referential.Partners().Find(core.PartnerId(identifier))
	if partner == nil {
		http.Error(response, fmt.Sprintf("Partner not found: %s", identifier), 500)
		return
	}
	logger.Log.Debugf("Delete partner %s", identifier)

	jsonBytes, _ := partner.MarshalJSON()
	controller.referential.Partners().Delete(partner)
	response.Write(jsonBytes)
}

func (controller *PartnerController) Update(response http.ResponseWriter, identifier string, body []byte) {
	partner := controller.referential.Partners().Find(core.PartnerId(identifier))
	if partner == nil {
		http.Error(response, fmt.Sprintf("Partner not found: %s", identifier), 500)
		return
	}

	logger.Log.Debugf("Update partner %s: %s", identifier, string(body))

	apiPartner := partner.Definition()
	err := json.Unmarshal(body, apiPartner)
	if err != nil {
		http.Error(response, "Invalid request: can't parse body", 400)
		return
	}

	if !apiPartner.Validate() {
		jsonBytes, _ := json.Marshal(apiPartner)
		response.WriteHeader(http.StatusBadRequest)
		response.Write(jsonBytes)
		return
	}

	partner.SetDefinition(apiPartner)
	partner.Save()
	jsonBytes, _ := partner.MarshalJSON()
	response.Write(jsonBytes)
}

func (controller *PartnerController) Create(response http.ResponseWriter, body []byte) {
	logger.Log.Debugf("Create partner: %s", string(body))

	partner := core.NewPartner()
	apiPartner := partner.Definition()
	err := json.Unmarshal(body, apiPartner)
	if err != nil {
		http.Error(response, "Invalid request: can't parse body", 400)
		return
	}
	if partner.Id() != "" {
		http.Error(response, "Invalid request (Id specified)", 400)
		return
	}

	if !apiPartner.Validate() {
		jsonBytes, _ := json.Marshal(apiPartner)
		response.WriteHeader(http.StatusBadRequest)
		response.Write(jsonBytes)
		return
	}

	partner.SetDefinition(apiPartner)
	controller.referential.Partners().Save(partner)
	jsonBytes, _ := partner.MarshalJSON()
	response.Write(jsonBytes)
}
<commit_msg>Fix validation for Id in partner controller<commit_after>package api

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

	"github.com/af83/edwig/core"
	"github.com/af83/edwig/logger"
)

type PartnerController struct {
	referential *core.Referential
}

func NewPartnerController(referential *core.Referential) *Controller {
	return &Controller{
		restfulRessource: &PartnerController{
			referential: referential,
		},
	}
}

func (controller *PartnerController) Index(response http.ResponseWriter) {
	logger.Log.Debugf("Partners Index")

	jsonBytes, _ := json.Marshal(controller.referential.Partners().FindAll())
	response.Write(jsonBytes)
}

func (controller *PartnerController) Show(response http.ResponseWriter, identifier string) {
	partner := controller.referential.Partners().Find(core.PartnerId(identifier))
	if partner == nil {
		http.Error(response, fmt.Sprintf("Partner not found: %s", identifier), 500)
		return
	}
	logger.Log.Debugf("Get partner %s", identifier)

	jsonBytes, _ := partner.MarshalJSON()
	response.Write(jsonBytes)
}

func (controller *PartnerController) Delete(response http.ResponseWriter, identifier string) {
	partner := controller.referential.Partners().Find(core.PartnerId(identifier))
	if partner == nil {
		http.Error(response, fmt.Sprintf("Partner not found: %s", identifier), 500)
		return
	}
	logger.Log.Debugf("Delete partner %s", identifier)

	jsonBytes, _ := partner.MarshalJSON()
	controller.referential.Partners().Delete(partner)
	response.Write(jsonBytes)
}

func (controller *PartnerController) Update(response http.ResponseWriter, identifier string, body []byte) {
	partner := controller.referential.Partners().Find(core.PartnerId(identifier))
	if partner == nil {
		http.Error(response, fmt.Sprintf("Partner not found: %s", identifier), 500)
		return
	}

	logger.Log.Debugf("Update partner %s: %s", identifier, string(body))

	apiPartner := partner.Definition()
	err := json.Unmarshal(body, apiPartner)
	if err != nil {
		http.Error(response, "Invalid request: can't parse body", 400)
		return
	}
	if apiPartner.Id != partner.Id() {
		http.Error(response, "Invalid request (Id specified)", 400)
		return
	}

	if !apiPartner.Validate() {
		jsonBytes, _ := json.Marshal(apiPartner)
		response.WriteHeader(http.StatusBadRequest)
		response.Write(jsonBytes)
		return
	}

	partner.SetDefinition(apiPartner)
	partner.Save()
	jsonBytes, _ := partner.MarshalJSON()
	response.Write(jsonBytes)
}

func (controller *PartnerController) Create(response http.ResponseWriter, body []byte) {
	logger.Log.Debugf("Create partner: %s", string(body))

	partner := core.NewPartner()
	apiPartner := partner.Definition()
	err := json.Unmarshal(body, apiPartner)
	if err != nil {
		http.Error(response, "Invalid request: can't parse body", 400)
		return
	}
	if apiPartner.Id != "" {
		http.Error(response, "Invalid request (Id specified)", 400)
		return
	}

	if !apiPartner.Validate() {
		jsonBytes, _ := json.Marshal(apiPartner)
		response.WriteHeader(http.StatusBadRequest)
		response.Write(jsonBytes)
		return
	}

	partner.SetDefinition(apiPartner)
	controller.referential.Partners().Save(partner)
	jsonBytes, _ := partner.MarshalJSON()
	response.Write(jsonBytes)
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2015 Red Hat, Inc.
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 *
 */

package elasticseach

import (
	"encoding/json"
	"errors"
	"strconv"
	"strings"

	elastigo "github.com/mattbaird/elastigo/lib"

	"github.com/redhat-cip/skydive/config"
	"github.com/redhat-cip/skydive/flow"
	"github.com/redhat-cip/skydive/logging"
	"github.com/redhat-cip/skydive/storage"
)

type ElasticSearchStorage struct {
	connection *elastigo.Conn
	indexer    *elastigo.BulkIndexer
}

func (c *ElasticSearchStorage) StoreFlows(flows []*flow.Flow) error {
	for _, flow := range flows {
		err := c.indexer.Index("skydive", "flow", flow.ID(), "", "", nil, flow)
		if err != nil {
			logging.GetLogger().Errorf("Error while indexing: %s", err.Error())
			continue
		}
	}

	return nil
}

func (c *ElasticSearchStorage) SearchFlows(filters storage.Filters) ([]*flow.Flow, error) {
	query := map[string]interface{}{
		"sort": map[string]interface{}{
			"Statistics.Last": map[string]string{
				"order": "desc",
			},
		},
		"from": 0,
		"size": 5,
	}
	if len(filters) > 0 {
		query = map[string]interface{}{
			"query": map[string]interface{}{
				"term": filters,
			},
			"sort": map[string]interface{}{
				"Statistics.Last": map[string]string{
					"order": "desc",
				},
			},
			"from": 0,
			"size": 5,
		}
	}

	q, err := json.Marshal(query)
	if err != nil {
		return nil, err
	}

	out, err := c.connection.Search("skydive", "flow", nil, string(q))
	if err != nil {
		return nil, err
	}

	flows := []*flow.Flow{}

	if out.Hits.Len() > 0 {
		for _, d := range out.Hits.Hits {
			f := new(flow.Flow)
			err := json.Unmarshal([]byte(*d.Source), f)
			if err != nil {
				return nil, err
			}

			flows = append(flows, f)
		}
	}

	return flows, nil
}

func (c *ElasticSearchStorage) initialize() error {
	c.indexer = c.connection.NewBulkIndexerErrors(10, 60)
	c.indexer.Start()

	req, err := c.connection.NewRequest("GET", "/skydive", "")
	if err != nil {
		return err
	}

	var response map[string]interface{}
	code, _, _ := req.Do(&response)
	if code == 200 {
		return nil
	}

	// template to remove the analyzer
	req, err = c.connection.NewRequest("PUT", "/skydive", "")
	if err != nil {
		return err
	}

	body := `{"mappings":{"flow":{"dynamic_templates":[`
	body += `{"notanalyzed_graph":{"match":"*Graph*","mapping":{"type":"string","index":"not_analyzed"}}},`
	body += `{"notanalyzed_graph":{"match":"*Layer*","mapping":{"type":"string","index":"not_analyzed"}}}`
	body += `]}}}`

	req.SetBodyString(body)

	code, _, err = req.Do(&response)
	if err != nil {
		return err
	}

	if code != 200 {
		return errors.New("Unable to create the skydive index: " + strconv.FormatInt(int64(code), 10))
	}

	return nil
}

var ErrBadConfig = errors.New("elasticseach : Config file is misconfigured, check elasticsearch key format")

func (c *ElasticSearchStorage) Close() {
	c.indexer.Stop()
	c.connection.Close()
}

func New() (*ElasticSearchStorage, error) {
	c := elastigo.NewConn()

	elasticonfig := strings.Split(config.GetConfig().GetString("storage.elasticsearch"), ":")
	if len(elasticonfig) != 2 {
		return nil, ErrBadConfig
	}
	c.Domain = elasticonfig[0]
	c.Port = elasticonfig[1]

	storage := &ElasticSearchStorage{connection: c}

	err := storage.initialize()
	if err != nil {
		return nil, err
	}

	return storage, nil
}
<commit_msg>ES, add a version + an alias to manage mapping changes<commit_after>/*
 * Copyright (C) 2015 Red Hat, Inc.
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 *
 */

package elasticseach

import (
	"encoding/json"
	"errors"
	"fmt"
	"strconv"
	"strings"

	elastigo "github.com/mattbaird/elastigo/lib"

	"github.com/redhat-cip/skydive/config"
	"github.com/redhat-cip/skydive/flow"
	"github.com/redhat-cip/skydive/logging"
	"github.com/redhat-cip/skydive/storage"
)

const indexVersion = 1

const mapping = `
{"mappings":{"flow":{"dynamic_templates":[
	{"notanalyzed_graph":{"match":"*GraphPath","mapping":{"type":"string","index":"not_analyzed"}}},
	{"notanalyzed_layers":{"match":"LayersPath","mapping":{"type":"string","index":"not_analyzed"}}},
	{"start_epoch":{"match":"Start","mapping":{"type":"date", "format": "epoch_second"}}},
	{"last_epoch":{"match":"Last","mapping":{"type":"date", "format": "epoch_second"}}}
]}}}
`

type ElasticSearchStorage struct {
	connection *elastigo.Conn
	indexer    *elastigo.BulkIndexer
}

func (c *ElasticSearchStorage) StoreFlows(flows []*flow.Flow) error {
	for _, flow := range flows {
		err := c.indexer.Index("skydive", "flow", flow.ID(), "", "", nil, flow)
		if err != nil {
			logging.GetLogger().Errorf("Error while indexing: %s", err.Error())
			continue
		}
	}

	return nil
}

func (c *ElasticSearchStorage) SearchFlows(filters storage.Filters) ([]*flow.Flow, error) {
	query := map[string]interface{}{
		"sort": map[string]interface{}{
			"Statistics.Last": map[string]string{
				"order": "desc",
			},
		},
		"from": 0,
		"size": 5,
	}
	if len(filters) > 0 {
		query = map[string]interface{}{
			"query": map[string]interface{}{
				"term": filters,
			},
			"sort": map[string]interface{}{
				"Statistics.Last": map[string]string{
					"order": "desc",
				},
			},
			"from": 0,
			"size": 5,
		}
	}

	q, err := json.Marshal(query)
	if err != nil {
		return nil, err
	}

	out, err := c.connection.Search("skydive", "flow", nil, string(q))
	if err != nil {
		return nil, err
	}

	flows := []*flow.Flow{}

	if out.Hits.Len() > 0 {
		for _, d := range out.Hits.Hits {
			f := new(flow.Flow)
			err := json.Unmarshal([]byte(*d.Source), f)
			if err != nil {
				return nil, err
			}

			flows = append(flows, f)
		}
	}

	return flows, nil
}

func (c *ElasticSearchStorage) request(method string, path string, query string, body string) (int, []byte, error) {
	req, err := c.connection.NewRequest(method, path, query)
	if err != nil {
		return 503, nil, err
	}

	if body != "" {
		req.SetBodyString(body)
	}

	var response map[string]interface{}
	return req.Do(&response)
}

func (c *ElasticSearchStorage) initialize() error {
	c.indexer = c.connection.NewBulkIndexerErrors(10, 60)
	c.indexer.Start()

	indexPath := fmt.Sprintf("/skydive_v%d", indexVersion)

	code, _, _ := c.request("GET", indexPath, "", "")
	if code == 200 {
		return nil
	}

	code, _, _ = c.request("PUT", indexPath, "", mapping)
	if code != 200 {
		return errors.New("Unable to create the skydive index: " + strconv.FormatInt(int64(code), 10))
	}

	aliases := `{"actions": [`

	code, data, _ := c.request("GET", "/_aliases", "", "")
	if code == 200 {
		var current map[string]interface{}

		err := json.Unmarshal(data, &current)
		if err != nil {
			return errors.New("Unable to parse aliases: " + err.Error())
		}

		for k := range current {
			if strings.HasPrefix(k, "skydive_") {
				remove := `{"remove":{"alias": "skydive", "index": "%s"}},`
				aliases += fmt.Sprintf(remove, k)
			}
		}
	}

	add := `{"add":{"alias": "skydive", "index": "skydive_v%d"}}]}`
	aliases += fmt.Sprintf(add, indexVersion)

	code, _, _ = c.request("POST", "/_aliases", "", aliases)
	if code != 200 {
		return errors.New("Unable to create an alias to the skydive index: " + strconv.FormatInt(int64(code), 10))
	}

	return nil
}

var ErrBadConfig = errors.New("elasticseach : Config file is misconfigured, check elasticsearch key format")

func (c *ElasticSearchStorage) Close() {
	c.indexer.Stop()
	c.connection.Close()
}

func New() (*ElasticSearchStorage, error) {
	c := elastigo.NewConn()

	elasticonfig := strings.Split(config.GetConfig().GetString("storage.elasticsearch"), ":")
	if len(elasticonfig) != 2 {
		return nil, ErrBadConfig
	}
	c.Domain = elasticonfig[0]
	c.Port = elasticonfig[1]

	storage := &ElasticSearchStorage{connection: c}

	err := storage.initialize()
	if err != nil {
		return nil, err
	}

	return storage, nil
}
<|endoftext|>


<commit_before>// Copyright 2016 Stratumn SAS. All rights reserved.
// Use of this source code is governed by an Apache License 2.0
// that can be found in the LICENSE file.

package storetesting

import (
	"reflect"
	"testing"

	"github.com/stratumn/go/cs"
	"github.com/stratumn/go/cs/cstesting"
	"github.com/stratumn/go/store"
)

func TestMockAdapter_GetInfo(t *testing.T) {
	a := &MockAdapter{}

	_, err := a.GetInfo()

	if err != nil {
		t.Fatal("unexpected error")
	}

	a.MockGetInfo.Fn = func() (interface{}, error) { return map[string]string{"name": "test"}, nil }
	info, err := a.GetInfo()

	if err != nil {
		t.Fatal("unexpected error")
	}

	if info.(map[string]string)["name"] != "test" {
		t.Fatal("unexpect info")
	}

	if a.MockGetInfo.CalledCount != 2 {
		t.Fatal("unexpected MockGetInfo.CalledCount value")
	}
}

func TestMockAdapter_GetSegment(t *testing.T) {
	a := &MockAdapter{}

	_, err := a.GetSegment("abcdef")

	if err != nil {
		t.Fatal("unexpected error")
	}

	s1 := cstesting.RandomSegment()
	a.MockGetSegment.Fn = func(linkHash string) (*cs.Segment, error) { return s1, nil }
	s2, err := a.GetSegment("ghij")

	if err != nil {
		t.Fatal("unexpected error")
	}

	if s1 != s2 {
		t.Fatal("expected segments to be equal")
	}

	if a.MockGetSegment.CalledCount != 2 {
		t.Fatal("unexpected MockGetSegment.CalledCount value")
	}

	if !reflect.DeepEqual(a.MockGetSegment.CalledWith, []string{"abcdef", "ghij"}) {
		t.Fatal("unexpected MockGetSegment.LastCalledWith value")
	}

	if a.MockGetSegment.LastCalledWith != "ghij" {
		t.Fatal("unexpected MockGetSegment.LastCalledWith value")
	}
}

func TestMockAdapter_DeleteSegment(t *testing.T) {
	a := &MockAdapter{}

	_, err := a.DeleteSegment("abcdef")

	if err != nil {
		t.Fatal("unexpected error")
	}

	s1 := cstesting.RandomSegment()
	a.MockDeleteSegment.Fn = func(linkHash string) (*cs.Segment, error) { return s1, nil }
	s2, err := a.DeleteSegment("ghij")

	if err != nil {
		t.Fatal("unexpected error")
	}

	if s1 != s2 {
		t.Fatal("expected segments to be equal")
	}

	if a.MockDeleteSegment.CalledCount != 2 {
		t.Fatal("unexpected MockDeleteSegment.CalledCount value")
	}

	if !reflect.DeepEqual(a.MockDeleteSegment.CalledWith, []string{"abcdef", "ghij"}) {
		t.Fatal("unexpected MockDeleteSegment.LastCalledWith value")
	}

	if a.MockDeleteSegment.LastCalledWith != "ghij" {
		t.Fatal("unexpected MockDeleteSegment.LastCalledWith value")
	}
}

func TestMockAdapter_FindSegments(t *testing.T) {
	a := &MockAdapter{}

	_, err := a.FindSegments(nil)

	if err != nil {
		t.Fatal("unexpected error")
	}

	s := cstesting.RandomSegment()
	a.MockFindSegments.Fn = func(*store.Filter) (cs.SegmentSlice, error) { return cs.SegmentSlice{s}, nil }
	f := store.Filter{PrevLinkHash: "test"}
	slice, err := a.FindSegments(&f)

	if err != nil {
		t.Fatal("unexpected error")
	}

	if !reflect.DeepEqual(slice, cs.SegmentSlice{s}) {
		t.Fatal("expected segment slices to be equal")
	}

	if a.MockFindSegments.CalledCount != 2 {
		t.Fatal("unexpected MockFindSegments.CalledCount value")
	}

	if !reflect.DeepEqual(a.MockFindSegments.CalledWith, []*store.Filter{nil, &f}) {
		t.Fatal("unexpected MockFindSegments.LastCalledWith value")
	}

	if a.MockFindSegments.LastCalledWith != &f {
		t.Fatal("unexpected MockFindSegments.LastCalledWith value")
	}
}

func TestMockAdapter_GetMapIDs(t *testing.T) {
	a := &MockAdapter{}

	_, err := a.GetMapIDs(nil)

	if err != nil {
		t.Fatal("unexpected error")
	}

	a.MockGetMapIDs.Fn = func(*store.Pagination) ([]string, error) { return []string{"one", "two"}, nil }
	p := store.Pagination{Offset: 10}
	slice, err := a.GetMapIDs(&p)

	if err != nil {
		t.Fatal("unexpected error")
	}

	if !reflect.DeepEqual(slice, []string{"one", "two"}) {
		t.Fatal("expected segment slices to be equal")
	}

	if a.MockGetMapIDs.CalledCount != 2 {
		t.Fatal("unexpected MockGetMapIDs.CalledCount value")
	}

	if !reflect.DeepEqual(a.MockGetMapIDs.CalledWith, []*store.Pagination{nil, &p}) {
		t.Fatal("unexpected MockGetMapIDs.LastCalledWith value")
	}

	if a.MockGetMapIDs.LastCalledWith != &p {
		t.Fatal("unexpected MockGetMapIDs.LastCalledWith value")
	}
}
<commit_msg>Add test for MockAdapter#SaveSegment<commit_after>// Copyright 2016 Stratumn SAS. All rights reserved.
// Use of this source code is governed by an Apache License 2.0
// that can be found in the LICENSE file.

package storetesting

import (
	"reflect"
	"testing"

	"github.com/stratumn/go/cs"
	"github.com/stratumn/go/cs/cstesting"
	"github.com/stratumn/go/store"
)

func TestMockAdapter_GetInfo(t *testing.T) {
	a := &MockAdapter{}

	_, err := a.GetInfo()

	if err != nil {
		t.Fatal("unexpected error")
	}

	a.MockGetInfo.Fn = func() (interface{}, error) { return map[string]string{"name": "test"}, nil }
	info, err := a.GetInfo()

	if err != nil {
		t.Fatal("unexpected error")
	}

	if info.(map[string]string)["name"] != "test" {
		t.Fatal("unexpect info")
	}

	if a.MockGetInfo.CalledCount != 2 {
		t.Fatal("unexpected MockGetInfo.CalledCount value")
	}
}

func TestMockAdapter_SaveSegment(t *testing.T) {
	a := &MockAdapter{}
	s := cstesting.RandomSegment()

	err := a.SaveSegment(s)

	if err != nil {
		t.Fatal("unexpected error")
	}

	a.MockSaveSegment.Fn = func(s *cs.Segment) error { return nil }
	err = a.SaveSegment(s)

	if err != nil {
		t.Fatal("unexpected error")
	}

	if a.MockSaveSegment.CalledCount != 2 {
		t.Fatal("unexpected MockSaveSegment.CalledCount value")
	}

	if !reflect.DeepEqual(a.MockSaveSegment.CalledWith, []*cs.Segment{s, s}) {
		t.Fatal("unexpected MockSaveSegment.LastCalledWith value")
	}

	if a.MockSaveSegment.LastCalledWith != s {
		t.Fatal("unexpected MockSaveSegment.LastCalledWith value")
	}
}

func TestMockAdapter_GetSegment(t *testing.T) {
	a := &MockAdapter{}

	_, err := a.GetSegment("abcdef")

	if err != nil {
		t.Fatal("unexpected error")
	}

	s1 := cstesting.RandomSegment()
	a.MockGetSegment.Fn = func(linkHash string) (*cs.Segment, error) { return s1, nil }
	s2, err := a.GetSegment("ghij")

	if err != nil {
		t.Fatal("unexpected error")
	}

	if s1 != s2 {
		t.Fatal("expected segments to be equal")
	}

	if a.MockGetSegment.CalledCount != 2 {
		t.Fatal("unexpected MockGetSegment.CalledCount value")
	}

	if !reflect.DeepEqual(a.MockGetSegment.CalledWith, []string{"abcdef", "ghij"}) {
		t.Fatal("unexpected MockGetSegment.LastCalledWith value")
	}

	if a.MockGetSegment.LastCalledWith != "ghij" {
		t.Fatal("unexpected MockGetSegment.LastCalledWith value")
	}
}

func TestMockAdapter_DeleteSegment(t *testing.T) {
	a := &MockAdapter{}

	_, err := a.DeleteSegment("abcdef")

	if err != nil {
		t.Fatal("unexpected error")
	}

	s1 := cstesting.RandomSegment()
	a.MockDeleteSegment.Fn = func(linkHash string) (*cs.Segment, error) { return s1, nil }
	s2, err := a.DeleteSegment("ghij")

	if err != nil {
		t.Fatal("unexpected error")
	}

	if s1 != s2 {
		t.Fatal("expected segments to be equal")
	}

	if a.MockDeleteSegment.CalledCount != 2 {
		t.Fatal("unexpected MockDeleteSegment.CalledCount value")
	}

	if !reflect.DeepEqual(a.MockDeleteSegment.CalledWith, []string{"abcdef", "ghij"}) {
		t.Fatal("unexpected MockDeleteSegment.LastCalledWith value")
	}

	if a.MockDeleteSegment.LastCalledWith != "ghij" {
		t.Fatal("unexpected MockDeleteSegment.LastCalledWith value")
	}
}

func TestMockAdapter_FindSegments(t *testing.T) {
	a := &MockAdapter{}

	_, err := a.FindSegments(nil)

	if err != nil {
		t.Fatal("unexpected error")
	}

	s := cstesting.RandomSegment()
	a.MockFindSegments.Fn = func(*store.Filter) (cs.SegmentSlice, error) { return cs.SegmentSlice{s}, nil }
	f := store.Filter{PrevLinkHash: "test"}
	slice, err := a.FindSegments(&f)

	if err != nil {
		t.Fatal("unexpected error")
	}

	if !reflect.DeepEqual(slice, cs.SegmentSlice{s}) {
		t.Fatal("expected segment slices to be equal")
	}

	if a.MockFindSegments.CalledCount != 2 {
		t.Fatal("unexpected MockFindSegments.CalledCount value")
	}

	if !reflect.DeepEqual(a.MockFindSegments.CalledWith, []*store.Filter{nil, &f}) {
		t.Fatal("unexpected MockFindSegments.LastCalledWith value")
	}

	if a.MockFindSegments.LastCalledWith != &f {
		t.Fatal("unexpected MockFindSegments.LastCalledWith value")
	}
}

func TestMockAdapter_GetMapIDs(t *testing.T) {
	a := &MockAdapter{}

	_, err := a.GetMapIDs(nil)

	if err != nil {
		t.Fatal("unexpected error")
	}

	a.MockGetMapIDs.Fn = func(*store.Pagination) ([]string, error) { return []string{"one", "two"}, nil }
	p := store.Pagination{Offset: 10}
	slice, err := a.GetMapIDs(&p)

	if err != nil {
		t.Fatal("unexpected error")
	}

	if !reflect.DeepEqual(slice, []string{"one", "two"}) {
		t.Fatal("expected segment slices to be equal")
	}

	if a.MockGetMapIDs.CalledCount != 2 {
		t.Fatal("unexpected MockGetMapIDs.CalledCount value")
	}

	if !reflect.DeepEqual(a.MockGetMapIDs.CalledWith, []*store.Pagination{nil, &p}) {
		t.Fatal("unexpected MockGetMapIDs.LastCalledWith value")
	}

	if a.MockGetMapIDs.LastCalledWith != &p {
		t.Fatal("unexpected MockGetMapIDs.LastCalledWith value")
	}
}
<|endoftext|>


<commit_before>package server

import (
	"bytes"
	"net/http"
	"strings"
	"testing"

	"gitlab-odx.oracle.com/odx/functions/api/datastore"
	"gitlab-odx.oracle.com/odx/functions/api/logs"
	"gitlab-odx.oracle.com/odx/functions/api/models"
	"gitlab-odx.oracle.com/odx/functions/api/mqs"
)

type routeTestCase struct {
	ds            models.Datastore
	logDB         models.FnLog
	method        string
	path          string
	body          string
	expectedCode  int
	expectedError error
}

func (test *routeTestCase) run(t *testing.T, i int, buf *bytes.Buffer) {
	rnr, cancel := testRunner(t)
	srv := testServer(test.ds, &mqs.Mock{}, test.logDB, rnr)

	body := bytes.NewBuffer([]byte(test.body))
	_, rec := routerRequest(t, srv.Router, test.method, test.path, body)

	if rec.Code != test.expectedCode {
		t.Log(buf.String())
		t.Errorf("Test %d: Expected status code to be %d but was %d",
			i, test.expectedCode, rec.Code)
	}

	if test.expectedError != nil {
		resp := getErrorResponse(t, rec)
		if resp.Error == nil {
			t.Log(buf.String())
			t.Errorf("Test %d: Expected error message to have `%s`, but it was nil",
				i, test.expectedError)
		} else if !strings.Contains(resp.Error.Message, test.expectedError.Error()) {
			t.Log(buf.String())
			t.Errorf("Test %d: Expected error message to have `%s`, but it was `%s`",
				i, test.expectedError, resp.Error.Message)
		}
	}
	cancel()
	buf.Reset()
}

func TestRouteCreate(t *testing.T) {
	buf := setLogBuffer()

	for i, test := range []routeTestCase{
		// errors
		{datastore.NewMock(), logs.NewMock(), http.MethodPost, "/v1/apps/a/routes", ``, http.StatusBadRequest, models.ErrInvalidJSON},
		{datastore.NewMock(), logs.NewMock(), http.MethodPost, "/v1/apps/a/routes", `{ }`, http.StatusBadRequest, models.ErrRoutesMissingNew},
		{datastore.NewMock(), logs.NewMock(), http.MethodPost, "/v1/apps/a/routes", `{ "path": "/myroute" }`, http.StatusBadRequest, models.ErrRoutesMissingNew},
		{datastore.NewMock(), logs.NewMock(), http.MethodPost, "/v1/apps/a/routes", `{ "route": { } }`, http.StatusBadRequest, models.ErrRoutesValidationMissingPath},
		{datastore.NewMock(), logs.NewMock(), http.MethodPost, "/v1/apps/a/routes", `{ "route": { "path": "/myroute" } }`, http.StatusBadRequest, models.ErrRoutesValidationMissingImage},
		{datastore.NewMock(), logs.NewMock(), http.MethodPost, "/v1/apps/a/routes", `{ "route": { "image": "funcy/hello" } }`, http.StatusBadRequest, models.ErrRoutesValidationMissingPath},
		{datastore.NewMock(), logs.NewMock(), http.MethodPost, "/v1/apps/a/routes", `{ "route": { "image": "funcy/hello", "path": "myroute" } }`, http.StatusBadRequest, models.ErrRoutesValidationInvalidPath},
		{datastore.NewMock(), logs.NewMock(), http.MethodPost, "/v1/apps/$/routes", `{ "route": { "image": "funcy/hello", "path": "/myroute" } }`, http.StatusBadRequest, models.ErrAppsValidationInvalidName},
		{datastore.NewMockInit(nil,
			[]*models.Route{
				{
					AppName: "a",
					Path:    "/myroute",
				},
			}, nil, nil,
		), logs.NewMock(), http.MethodPost, "/v1/apps/a/routes", `{ "route": { "image": "funcy/hello", "path": "/myroute" } }`, http.StatusConflict, models.ErrRoutesAlreadyExists},

		// success
		{datastore.NewMock(), logs.NewMock(), http.MethodPost, "/v1/apps/a/routes", `{ "route": { "image": "funcy/hello", "path": "/myroute" } }`, http.StatusOK, nil},
	} {
		test.run(t, i, buf)
	}
}

func TestRoutePut(t *testing.T) {
	buf := setLogBuffer()

	for i, test := range []routeTestCase{
		// errors (NOTE: this route doesn't exist yet)
		{datastore.NewMock(), logs.NewMock(), http.MethodPut, "/v1/apps/a/routes/myroute", `{ }`, http.StatusBadRequest, models.ErrRoutesMissingNew},
		{datastore.NewMock(), logs.NewMock(), http.MethodPut, "/v1/apps/a/routes/myroute", `{ "path": "/myroute" }`, http.StatusBadRequest, models.ErrRoutesMissingNew},
		{datastore.NewMock(), logs.NewMock(), http.MethodPut, "/v1/apps/a/routes/myroute", `{ "route": { } }`, http.StatusBadRequest, models.ErrRoutesValidationMissingImage},
		{datastore.NewMock(), logs.NewMock(), http.MethodPut, "/v1/apps/a/routes/myroute", `{ "route": { "path": "/myroute" } }`, http.StatusBadRequest, models.ErrRoutesValidationMissingImage},
		{datastore.NewMock(), logs.NewMock(), http.MethodPut, "/v1/apps/a/routes/myroute", `{ "route": { "image": "funcy/hello", "path": "myroute" } }`, http.StatusConflict, models.ErrRoutesPathImmutable},
		{datastore.NewMock(), logs.NewMock(), http.MethodPut, "/v1/apps/a/routes/myroute", `{ "route": { "image": "funcy/hello", "path": "diffRoute" } }`, http.StatusConflict, models.ErrRoutesPathImmutable},
		{datastore.NewMock(), logs.NewMock(), http.MethodPut, "/v1/apps/$/routes/myroute", `{ "route": { "image": "funcy/hello", "path": "/myroute" } }`, http.StatusBadRequest, models.ErrAppsValidationInvalidName},
		{datastore.NewMock(), logs.NewMock(), http.MethodPut, "/v1/apps/a/routes/myroute", `{ "route": { "image": "funcy/hello", "path": "/myroute", "type": "invalid-type" } }`, http.StatusBadRequest, models.ErrRoutesValidationInvalidType},
		{datastore.NewMock(), logs.NewMock(), http.MethodPut, "/v1/apps/a/routes/myroute", `{ "route": { "image": "funcy/hello", "path": "/myroute", "format": "invalid-format" } }`, http.StatusBadRequest, models.ErrRoutesValidationInvalidFormat},

		// success
		{datastore.NewMock(), logs.NewMock(), http.MethodPut, "/v1/apps/a/routes/myroute", `{ "route": { "image": "funcy/hello", "path": "/myroute" } }`, http.StatusOK, nil},
		{datastore.NewMock(), logs.NewMock(), http.MethodPut, "/v1/apps/a/routes/myroute", `{ "route": { "image": "funcy/hello" } }`, http.StatusOK, nil},
	} {
		test.run(t, i, buf)
		test.ds = datastore.NewMockInit(nil,
			[]*models.Route{
				{
					AppName: "a",
					Path:    "/myroute",
				},
			}, nil, nil,
		)
		test.run(t, i, buf)
	}
}

func TestRouteDelete(t *testing.T) {
	buf := setLogBuffer()

	routes := models.Routes{{AppName: "a", Path: "/myroute"}}
	apps := []*models.App{{Name: "a", Routes: routes, Config: nil}}

	for i, test := range []struct {
		ds            models.Datastore
		logDB         models.FnLog
		path          string
		body          string
		expectedCode  int
		expectedError error
	}{
		{datastore.NewMock(), logs.NewMock(), "/v1/apps/a/routes/missing", "", http.StatusNotFound, models.ErrAppsNotFound},
		{datastore.NewMockInit(apps, routes, nil, nil), logs.NewMock(), "/v1/apps/a/routes/myroute", "", http.StatusOK, nil},
	} {
		rnr, cancel := testRunner(t)
		srv := testServer(test.ds, &mqs.Mock{}, test.logDB, rnr)
		_, rec := routerRequest(t, srv.Router, "DELETE", test.path, nil)

		if rec.Code != test.expectedCode {
			t.Log(buf.String())
			t.Errorf("Test %d: Expected status code to be %d but was %d",
				i, test.expectedCode, rec.Code)
		}

		if test.expectedError != nil {
			resp := getErrorResponse(t, rec)

			if !strings.Contains(resp.Error.Message, test.expectedError.Error()) {
				t.Log(buf.String())
				t.Errorf("Test %d: Expected error message to have `%s`",
					i, test.expectedError.Error())
			}
		}
		cancel()
	}
}

func TestRouteList(t *testing.T) {
	buf := setLogBuffer()

	rnr, cancel := testRunner(t)
	defer cancel()

	ds := datastore.NewMock()
	fnl := logs.NewMock()

	srv := testServer(ds, &mqs.Mock{}, fnl, rnr)

	for i, test := range []struct {
		path          string
		body          string
		expectedCode  int
		expectedError error
	}{
		{"/v1/apps/a/routes", "", http.StatusOK, nil},
	} {
		_, rec := routerRequest(t, srv.Router, "GET", test.path, nil)

		if rec.Code != test.expectedCode {
			t.Log(buf.String())
			t.Errorf("Test %d: Expected status code to be %d but was %d",
				i, test.expectedCode, rec.Code)
		}

		if test.expectedError != nil {
			resp := getErrorResponse(t, rec)

			if !strings.Contains(resp.Error.Message, test.expectedError.Error()) {
				t.Log(buf.String())
				t.Errorf("Test %d: Expected error message to have `%s`",
					i, test.expectedError.Error())
			}
		}
	}
}

func TestRouteGet(t *testing.T) {
	buf := setLogBuffer()

	rnr, cancel := testRunner(t)
	defer cancel()

	ds := datastore.NewMock()
	fnl := logs.NewMock()

	srv := testServer(ds, &mqs.Mock{}, fnl, rnr)

	for i, test := range []struct {
		path          string
		body          string
		expectedCode  int
		expectedError error
	}{
		{"/v1/apps/a/routes/myroute", "", http.StatusNotFound, nil},
	} {
		_, rec := routerRequest(t, srv.Router, "GET", test.path, nil)

		if rec.Code != test.expectedCode {
			t.Log(buf.String())
			t.Errorf("Test %d: Expected status code to be %d but was %d",
				i, test.expectedCode, rec.Code)
		}

		if test.expectedError != nil {
			resp := getErrorResponse(t, rec)

			if !strings.Contains(resp.Error.Message, test.expectedError.Error()) {
				t.Log(buf.String())
				t.Errorf("Test %d: Expected error message to have `%s`",
					i, test.expectedError.Error())
			}
		}
	}
}

func TestRouteUpdate(t *testing.T) {
	buf := setLogBuffer()

	for i, test := range []routeTestCase{
		// errors
		{datastore.NewMock(), logs.NewMock(), http.MethodPatch, "/v1/apps/a/routes/myroute/do", ``, http.StatusBadRequest, models.ErrInvalidJSON},
		{datastore.NewMock(), logs.NewMock(), http.MethodPatch, "/v1/apps/a/routes/myroute/do", `{}`, http.StatusBadRequest, models.ErrRoutesMissingNew},
		{datastore.NewMock(), logs.NewMock(), http.MethodPatch, "/v1/apps/a/routes/myroute/do", `{ "route": { "type": "invalid-type" } }`, http.StatusBadRequest, models.ErrRoutesValidationInvalidType},
		{datastore.NewMock(), logs.NewMock(), http.MethodPatch, "/v1/apps/a/routes/myroute/do", `{ "route": { "format": "invalid-format" } }`, http.StatusBadRequest, models.ErrRoutesValidationInvalidFormat},

		// success
		{datastore.NewMockInit(nil,
			[]*models.Route{
				{
					AppName: "a",
					Path:    "/myroute/do",
				},
			}, nil, nil,
		), logs.NewMock(), http.MethodPatch, "/v1/apps/a/routes/myroute/do", `{ "route": { "image": "funcy/hello" } }`, http.StatusOK, nil},

		// Addresses #381
		{datastore.NewMockInit(nil,
			[]*models.Route{
				{
					AppName: "a",
					Path:    "/myroute/do",
				},
			}, nil, nil,
		), logs.NewMock(), http.MethodPatch, "/v1/apps/a/routes/myroute/do", `{ "route": { "path": "/otherpath" } }`, http.StatusConflict, models.ErrRoutesPathImmutable},
	} {
		test.run(t, i, buf)
	}
}
<commit_msg>Fixing tests<commit_after>package server

import (
	"bytes"
	"net/http"
	"strings"
	"testing"

	"gitlab-odx.oracle.com/odx/functions/api/datastore"
	"gitlab-odx.oracle.com/odx/functions/api/logs"
	"gitlab-odx.oracle.com/odx/functions/api/models"
	"gitlab-odx.oracle.com/odx/functions/api/mqs"
)

type routeTestCase struct {
	ds            models.Datastore
	logDB         models.FnLog
	method        string
	path          string
	body          string
	expectedCode  int
	expectedError error
}

func (test *routeTestCase) run(t *testing.T, i int, buf *bytes.Buffer) {
	rnr, cancel := testRunner(t)
	srv := testServer(test.ds, &mqs.Mock{}, test.logDB, rnr)

	body := bytes.NewBuffer([]byte(test.body))
	_, rec := routerRequest(t, srv.Router, test.method, test.path, body)

	if rec.Code != test.expectedCode {
		t.Log(buf.String())
		t.Errorf("Test %d: Expected status code to be %d but was %d",
			i, test.expectedCode, rec.Code)
	}

	if test.expectedError != nil {
		resp := getErrorResponse(t, rec)
		if resp.Error == nil {
			t.Log(buf.String())
			t.Errorf("Test %d: Expected error message to have `%s`, but it was nil",
				i, test.expectedError)
		} else if !strings.Contains(resp.Error.Message, test.expectedError.Error()) {
			t.Log(buf.String())
			t.Errorf("Test %d: Expected error message to have `%s`, but it was `%s`",
				i, test.expectedError, resp.Error.Message)
		}
	}
	cancel()
	buf.Reset()
}

func TestRouteCreate(t *testing.T) {
	buf := setLogBuffer()

	for i, test := range []routeTestCase{
		// errors
		{datastore.NewMock(), logs.NewMock(), http.MethodPost, "/v1/apps/a/routes", ``, http.StatusBadRequest, models.ErrInvalidJSON},
		{datastore.NewMock(), logs.NewMock(), http.MethodPost, "/v1/apps/a/routes", `{ }`, http.StatusBadRequest, models.ErrRoutesMissingNew},
		{datastore.NewMock(), logs.NewMock(), http.MethodPost, "/v1/apps/a/routes", `{ "path": "/myroute" }`, http.StatusBadRequest, models.ErrRoutesMissingNew},
		{datastore.NewMock(), logs.NewMock(), http.MethodPost, "/v1/apps/a/routes", `{ "route": { } }`, http.StatusBadRequest, models.ErrRoutesValidationMissingPath},
		{datastore.NewMock(), logs.NewMock(), http.MethodPost, "/v1/apps/a/routes", `{ "route": { "path": "/myroute" } }`, http.StatusBadRequest, models.ErrRoutesValidationMissingImage},
		{datastore.NewMock(), logs.NewMock(), http.MethodPost, "/v1/apps/a/routes", `{ "route": { "image": "funcy/hello" } }`, http.StatusBadRequest, models.ErrRoutesValidationMissingPath},
		{datastore.NewMock(), logs.NewMock(), http.MethodPost, "/v1/apps/a/routes", `{ "route": { "image": "funcy/hello", "path": "myroute" } }`, http.StatusBadRequest, models.ErrRoutesValidationInvalidPath},
		{datastore.NewMock(), logs.NewMock(), http.MethodPost, "/v1/apps/$/routes", `{ "route": { "image": "funcy/hello", "path": "/myroute" } }`, http.StatusBadRequest, models.ErrAppsValidationInvalidName},
		{datastore.NewMockInit(nil,
			[]*models.Route{
				{
					AppName: "a",
					Path:    "/myroute",
				},
			}, nil, nil,
		), logs.NewMock(), http.MethodPost, "/v1/apps/a/routes", `{ "route": { "image": "funcy/hello", "path": "/myroute" } }`, http.StatusConflict, models.ErrRoutesAlreadyExists},

		// success
		{datastore.NewMock(), logs.NewMock(), http.MethodPost, "/v1/apps/a/routes", `{ "route": { "image": "funcy/hello", "path": "/myroute" } }`, http.StatusOK, nil},
	} {
		test.run(t, i, buf)
	}
}

func TestRoutePut(t *testing.T) {
	buf := setLogBuffer()

	for i, test := range []routeTestCase{
		// errors (NOTE: this route doesn't exist yet)
		{datastore.NewMock(), logs.NewMock(), http.MethodPut, "/v1/apps/a/routes/myroute", `{ }`, http.StatusBadRequest, models.ErrRoutesMissingNew},
		{datastore.NewMock(), logs.NewMock(), http.MethodPut, "/v1/apps/a/routes/myroute", `{ "path": "/myroute" }`, http.StatusBadRequest, models.ErrRoutesMissingNew},
		{datastore.NewMock(), logs.NewMock(), http.MethodPut, "/v1/apps/a/routes/myroute", `{ "route": { } }`, http.StatusBadRequest, models.ErrRoutesValidationMissingImage},
		{datastore.NewMock(), logs.NewMock(), http.MethodPut, "/v1/apps/a/routes/myroute", `{ "route": { "path": "/myroute" } }`, http.StatusBadRequest, models.ErrRoutesValidationMissingImage},
		{datastore.NewMock(), logs.NewMock(), http.MethodPut, "/v1/apps/a/routes/myroute", `{ "route": { "image": "funcy/hello", "path": "myroute" } }`, http.StatusConflict, models.ErrRoutesPathImmutable},
		{datastore.NewMock(), logs.NewMock(), http.MethodPut, "/v1/apps/a/routes/myroute", `{ "route": { "image": "funcy/hello", "path": "diffRoute" } }`, http.StatusConflict, models.ErrRoutesPathImmutable},
		{datastore.NewMock(), logs.NewMock(), http.MethodPut, "/v1/apps/$/routes/myroute", `{ "route": { "image": "funcy/hello", "path": "/myroute" } }`, http.StatusBadRequest, models.ErrAppsValidationInvalidName},
		{datastore.NewMock(), logs.NewMock(), http.MethodPut, "/v1/apps/a/routes/myroute", `{ "route": { "image": "funcy/hello", "path": "/myroute", "type": "invalid-type" } }`, http.StatusBadRequest, models.ErrRoutesValidationInvalidType},
		{datastore.NewMock(), logs.NewMock(), http.MethodPut, "/v1/apps/a/routes/myroute", `{ "route": { "image": "funcy/hello", "path": "/myroute", "format": "invalid-format" } }`, http.StatusBadRequest, models.ErrRoutesValidationInvalidFormat},

		// success
		{datastore.NewMock(), logs.NewMock(), http.MethodPut, "/v1/apps/a/routes/myroute", `{ "route": { "image": "funcy/hello", "path": "/myroute" } }`, http.StatusOK, nil},
		{datastore.NewMock(), logs.NewMock(), http.MethodPut, "/v1/apps/a/routes/myroute", `{ "route": { "image": "funcy/hello" } }`, http.StatusOK, nil},
	} {
		test.run(t, i, buf)
		test.ds = datastore.NewMockInit(nil,
			[]*models.Route{
				{
					AppName: "a",
					Path:    "/myroute",
				},
			}, nil, nil,
		)
		test.run(t, i, buf)
	}
}

func TestRouteDelete(t *testing.T) {
	buf := setLogBuffer()

	routes := models.Routes{{AppName: "a", Path: "/myroute"}}
	apps := models.Apps{{Name: "a", Routes: routes, Config: nil}}

	for i, test := range []struct {
		ds            models.Datastore
		logDB         models.FnLog
		path          string
		body          string
		expectedCode  int
		expectedError error
	}{
		{datastore.NewMock(), logs.NewMock(), "/v1/apps/a/routes/missing", "", http.StatusNotFound, models.ErrRoutesNotFound},
		{datastore.NewMockInit(apps, routes, nil, nil), logs.NewMock(), "/v1/apps/a/routes/myroute", "", http.StatusOK, nil},
	} {
		rnr, cancel := testRunner(t)
		srv := testServer(test.ds, &mqs.Mock{}, test.logDB, rnr)
		_, rec := routerRequest(t, srv.Router, "DELETE", test.path, nil)

		if rec.Code != test.expectedCode {
			t.Log(buf.String())
			t.Errorf("Test %d: Expected status code to be %d but was %d",
				i, test.expectedCode, rec.Code)
		}

		if test.expectedError != nil {
			resp := getErrorResponse(t, rec)

			if !strings.Contains(resp.Error.Message, test.expectedError.Error()) {
				t.Log(buf.String())
				t.Errorf("Test %d: Expected error message to have `%s`",
					i, test.expectedError.Error())
			}
		}
		cancel()
	}
}

func TestRouteList(t *testing.T) {
	buf := setLogBuffer()

	rnr, cancel := testRunner(t)
	defer cancel()

	ds := datastore.NewMock()
	fnl := logs.NewMock()

	srv := testServer(ds, &mqs.Mock{}, fnl, rnr)

	for i, test := range []struct {
		path          string
		body          string
		expectedCode  int
		expectedError error
	}{
		{"/v1/apps/a/routes", "", http.StatusNotFound, models.ErrAppsNotFound},
	} {
		_, rec := routerRequest(t, srv.Router, "GET", test.path, nil)

		if rec.Code != test.expectedCode {
			t.Log(buf.String())
			t.Errorf("Test %d: Expected status code to be %d but was %d",
				i, test.expectedCode, rec.Code)
		}

		if test.expectedError != nil {
			resp := getErrorResponse(t, rec)

			if !strings.Contains(resp.Error.Message, test.expectedError.Error()) {
				t.Log(buf.String())
				t.Errorf("Test %d: Expected error message to have `%s`",
					i, test.expectedError.Error())
			}
		}
	}
}

func TestRouteGet(t *testing.T) {
	buf := setLogBuffer()

	rnr, cancel := testRunner(t)
	defer cancel()

	ds := datastore.NewMock()
	fnl := logs.NewMock()

	srv := testServer(ds, &mqs.Mock{}, fnl, rnr)

	for i, test := range []struct {
		path          string
		body          string
		expectedCode  int
		expectedError error
	}{
		{"/v1/apps/a/routes/myroute", "", http.StatusNotFound, nil},
	} {
		_, rec := routerRequest(t, srv.Router, "GET", test.path, nil)

		if rec.Code != test.expectedCode {
			t.Log(buf.String())
			t.Errorf("Test %d: Expected status code to be %d but was %d",
				i, test.expectedCode, rec.Code)
		}

		if test.expectedError != nil {
			resp := getErrorResponse(t, rec)

			if !strings.Contains(resp.Error.Message, test.expectedError.Error()) {
				t.Log(buf.String())
				t.Errorf("Test %d: Expected error message to have `%s`",
					i, test.expectedError.Error())
			}
		}
	}
}

func TestRouteUpdate(t *testing.T) {
	buf := setLogBuffer()

	for i, test := range []routeTestCase{
		// errors
		{datastore.NewMock(), logs.NewMock(), http.MethodPatch, "/v1/apps/a/routes/myroute/do", ``, http.StatusBadRequest, models.ErrInvalidJSON},
		{datastore.NewMock(), logs.NewMock(), http.MethodPatch, "/v1/apps/a/routes/myroute/do", `{}`, http.StatusBadRequest, models.ErrRoutesMissingNew},
		{datastore.NewMock(), logs.NewMock(), http.MethodPatch, "/v1/apps/a/routes/myroute/do", `{ "route": { "type": "invalid-type" } }`, http.StatusBadRequest, models.ErrRoutesValidationInvalidType},
		{datastore.NewMock(), logs.NewMock(), http.MethodPatch, "/v1/apps/a/routes/myroute/do", `{ "route": { "format": "invalid-format" } }`, http.StatusBadRequest, models.ErrRoutesValidationInvalidFormat},

		// success
		{datastore.NewMockInit(nil,
			[]*models.Route{
				{
					AppName: "a",
					Path:    "/myroute/do",
				},
			}, nil, nil,
		), logs.NewMock(), http.MethodPatch, "/v1/apps/a/routes/myroute/do", `{ "route": { "image": "funcy/hello" } }`, http.StatusOK, nil},

		// Addresses #381
		{datastore.NewMockInit(nil,
			[]*models.Route{
				{
					AppName: "a",
					Path:    "/myroute/do",
				},
			}, nil, nil,
		), logs.NewMock(), http.MethodPatch, "/v1/apps/a/routes/myroute/do", `{ "route": { "path": "/otherpath" } }`, http.StatusConflict, models.ErrRoutesPathImmutable},
	} {
		test.run(t, i, buf)
	}
}
<|endoftext|>


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

package views

import (
	"encoding/json"
	"strings"

	"github.com/ernestio/api-gateway/models"
	"log"

	graph "gopkg.in/r3labs/graph.v2"
)

// ServiceRender : Service representation to be rendered on the frontend
type ServiceRender struct {
	ID              string              `json:"id"`
	DatacenterID    int                 `json:"datacenter_id"`
	Name            string              `json:"name"`
	Version         string              `json:"version"`
	Status          string              `json:"status"`
	UserID          int                 `json:"user_id"`
	UserName        string              `json:"user_name"`
	LastKnownError  string              `json:"last_known_error"`
	Options         string              `json:"options"`
	Definition      string              `json:"definition"`
	Vpcs            []map[string]string `json:"vpcs"`
	Networks        []map[string]string `json:"networks"`
	Instances       []map[string]string `json:"instances"`
	Nats            []map[string]string `json:"nats"`
	SecurityGroups  []map[string]string `json:"security_groups"`
	Elbs            []map[string]string `json:"elbs"`
	RDSClusters     []map[string]string `json:"rds_clusters"`
	RDSInstances    []map[string]string `json:"rds_instances"`
	EBSVolumes      []map[string]string `json:"ebs_volumes"`
	LoadBalancers   []map[string]string `json:"load_balancers"`
	SQLDatabases    []map[string]string `json:"sql_databases"`
	VirtualMachines []map[string]string `json:"virtual_machines"`
}

// Render : Map a Service to a ServiceRender
func (o *ServiceRender) Render(s models.Service) (err error) {
	o.ID = s.ID
	o.DatacenterID = s.DatacenterID
	o.Name = s.Name
	o.Version = s.Version.String()
	o.Status = s.Status
	o.UserID = s.UserID
	o.UserName = s.UserName
	if def, ok := s.Definition.(string); ok == true {
		o.Definition = def
	}

	g, err := s.Mapping()
	if err != nil {
		log.Println(err.Error())
		return err
	}

	o.Vpcs = RenderVpcs(g)
	o.Networks = RenderNetworks(g)
	o.SecurityGroups = RenderSecurityGroups(g)
	o.Nats = RenderNats(g)
	o.Instances = RenderInstances(g)
	o.Elbs = RenderELBs(g)
	o.RDSClusters = RenderRDSClusters(g)
	o.RDSInstances = RenderRDSInstances(g)
	o.EBSVolumes = RenderEBSVolumes(g)
	o.LoadBalancers = RenderLoadBalancers(g)
	o.SQLDatabases = RenderSQLDatabases(g)
	o.VirtualMachines = RenderVirtualMachines(g)

	return err
}

// RenderVpcs : renders a services vpcs
func RenderVpcs(g *graph.Graph) []map[string]string {
	var vpcs []map[string]string

	for _, n := range g.GetComponents().ByType("vpc") {
		gc := n.(*graph.GenericComponent)
		name, _ := (*gc)["name"].(string)
		id, _ := (*gc)["vpc_aws_id"].(string)
		subnet, _ := (*gc)["vpc_subnet"].(string)
		vpcs = append(vpcs, map[string]string{
			"name":       name,
			"vpc_id":     id,
			"vpc_subnet": subnet,
		})
	}

	return vpcs
}

// RenderNetworks : renders a services networks
func RenderNetworks(g *graph.Graph) []map[string]string {
	var networks []map[string]string

	for _, n := range g.GetComponents().ByType("network") {
		gc := n.(*graph.GenericComponent)
		name, _ := (*gc)["name"].(string)
		id, _ := (*gc)["network_aws_id"].(string)
		az, _ := (*gc)["availablity_zone"].(string)
		networks = append(networks, map[string]string{
			"name":              name,
			"network_aws_id":    id,
			"availability_zone": az,
		})
	}

	return networks
}

// RenderSecurityGroups : renders a services security groups
func RenderSecurityGroups(g *graph.Graph) []map[string]string {
	var sgs []map[string]string

	for _, n := range g.GetComponents().ByType("firewall") {
		gc := n.(*graph.GenericComponent)
		name, _ := (*gc)["name"].(string)
		id, _ := (*gc)["security_group_aws_id"].(string)
		sgs = append(sgs, map[string]string{
			"name":                  name,
			"security_group_aws_id": id,
		})
	}

	return sgs
}

// RenderNats : renders a services nat gateways
func RenderNats(g *graph.Graph) []map[string]string {
	var nats []map[string]string

	for _, n := range g.GetComponents().ByType("nat_gateway") {
		gc := n.(*graph.GenericComponent)
		name, _ := (*gc)["name"].(string)
		id, _ := (*gc)["nat_gateway_aws_id"].(string)
		nats = append(nats, map[string]string{
			"name":               name,
			"nat_gateway_aws_id": id,
		})
	}

	return nats
}

// RenderELBs : renders a services elbs
func RenderELBs(g *graph.Graph) []map[string]string {
	var elbs []map[string]string

	for _, n := range g.GetComponents().ByType("elb") {
		gc := n.(*graph.GenericComponent)
		name, _ := (*gc)["name"].(string)
		dns, _ := (*gc)["dns_name"].(string)
		elbs = append(elbs, map[string]string{
			"name":     name,
			"dns_name": dns,
		})
	}

	return elbs
}

// RenderInstances : renders a services instances
func RenderInstances(g *graph.Graph) []map[string]string {
	var instances []map[string]string

	for _, n := range g.GetComponents().ByType("instance") {
		gc := n.(*graph.GenericComponent)
		name, _ := (*gc)["name"].(string)
		id, _ := (*gc)["instance_aws_id"].(string)
		pip, _ := (*gc)["public_ip"].(string)
		ip, _ := (*gc)["ip"].(string)
		instances = append(instances, map[string]string{
			"name":            name,
			"instance_aws_id": id,
			"public_ip":       pip,
			"ip":              ip,
		})
	}

	return instances
}

// RenderRDSClusters : renders a services rds clusters
func RenderRDSClusters(g *graph.Graph) []map[string]string {
	var rdss []map[string]string

	for _, n := range g.GetComponents().ByType("rds_cluster") {
		gc := n.(*graph.GenericComponent)
		name, _ := (*gc)["name"].(string)
		endpoint, _ := (*gc)["endpoint"].(string)
		rdss = append(rdss, map[string]string{
			"name":     name,
			"endpoint": endpoint,
		})
	}

	return rdss
}

// RenderRDSInstances : renders a services rds instances
func RenderRDSInstances(g *graph.Graph) []map[string]string {
	var rdss []map[string]string

	for _, n := range g.GetComponents().ByType("rds_instance") {
		gc := n.(*graph.GenericComponent)
		name, _ := (*gc)["name"].(string)
		endpoint, _ := (*gc)["endpoint"].(string)
		rdss = append(rdss, map[string]string{
			"name":     name,
			"endpoint": endpoint,
		})
	}

	return rdss
}

// RenderEBSVolumes : renders a services ebs volumes
func RenderEBSVolumes(g *graph.Graph) []map[string]string {
	var rdss []map[string]string

	for _, n := range g.GetComponents().ByType("ebs_volume") {
		gc := n.(*graph.GenericComponent)
		name, _ := (*gc)["name"].(string)
		id, _ := (*gc)["volume_aws_id"].(string)
		rdss = append(rdss, map[string]string{
			"name":          name,
			"volume_aws_id": id,
		})
	}

	return rdss
}

// RenderLoadBalancers : renders load balancers
func RenderLoadBalancers(g *graph.Graph) []map[string]string {
	return renderResources(g, "lb", func(gc *graph.GenericComponent) map[string]string {
		name, _ := (*gc)["name"].(string)
		id, _ := (*gc)["id"].(string)
		configs, _ := (*gc)["frontend_ip_configurations"].([]interface{})
		cfg, _ := configs[0].(map[string]string)

		return map[string]string{
			"name":      name,
			"id":        id,
			"public_ip": cfg["public_ip_address"],
		}
	})
}

// RenderVirtualMachines : renders virtual machines
func RenderVirtualMachines(g *graph.Graph) []map[string]string {
	var resources []map[string]string
	mappedIPs := make(map[string]interface{}, 0)
	existingIPs := make(map[string]string, 0)

	for _, ip := range g.GetComponents().ByType("public_ip") {
		gc := ip.(*graph.GenericComponent)
		id, _ := (*gc)["id"].(string)
		ipAddress, _ := (*gc)["ip_address"].(string)
		existingIPs[id] = ipAddress
	}

	for _, ni := range g.GetComponents().ByType("network_interface") {
		var public []string
		var private []string

		gc := ni.(*graph.GenericComponent)
		name, _ := (*gc)["name"].(string)
		ips := make(map[string][]string)

		configs, _ := (*gc)["ip_configuration"].([]interface{})
		for _, cfg := range configs {
			c, _ := cfg.(map[string]interface{})
			pubID, _ := c["public_ip_address_id"].(string)
			pri, _ := c["private_ip_address"].(string)
			if pub, ok := existingIPs[pubID]; ok {
				public = append(public, pub)
			}
			private = append(private, pri)
		}

		ips["public"] = public
		ips["private"] = private
		mappedIPs[name] = make(map[string][]string, 0)
		mappedIPs[name] = ips
	}

	for _, n := range g.GetComponents().ByType("virtual_machine") {
		gc := n.(*graph.GenericComponent)
		name, _ := (*gc)["name"].(string)
		id, _ := (*gc)["id"].(string)
		networks, _ := (*gc)["network_interfaces"].([]interface{})
		publicIPs := make([]string, 0)
		privateIPs := make([]string, 0)
		for _, ni := range networks {
			netName := ni.(string)
			if val, ok := mappedIPs[netName]; ok {
				ips, _ := val.(map[string][]string)
				publicIPs = append(publicIPs, ips["public"]...)
				privateIPs = append(privateIPs, ips["private"]...)
			}
		}

		resources = append(resources, map[string]string{
			"name":       name,
			"id":         id,
			"public_ip":  strings.Join(publicIPs, ", "),
			"private_ip": strings.Join(privateIPs, ", "),
		})
	}

	return resources
}

// RenderSQLDatabases : renders sql databases
func RenderSQLDatabases(g *graph.Graph) []map[string]string {
	return renderResources(g, "sql_database", func(gc *graph.GenericComponent) map[string]string {
		name, _ := (*gc)["name"].(string)
		server, _ := (*gc)["server_name"].(string)
		id, _ := (*gc)["id"].(string)

		return map[string]string{
			"name":        name,
			"server_name": server,
			"id":          id,
		}
	})
}

type convert func(*graph.GenericComponent) map[string]string

func renderResources(g *graph.Graph, resourceType string, f convert) (resources []map[string]string) {
	for _, n := range g.GetComponents().ByType(resourceType) {
		gc := n.(*graph.GenericComponent)
		resources = append(resources, f(gc))
	}

	return
}

// RenderCollection : Maps a collection of Service on a collection of ServiceRender
func (o *ServiceRender) RenderCollection(services []models.Service) (list []ServiceRender, err error) {
	for _, s := range services {
		var output ServiceRender
		if err := output.Render(s); err == nil {
			list = append(list, output)
		}
	}

	return list, nil
}

// ToJSON : Converts a ServiceRender to json string
func (o *ServiceRender) ToJSON() ([]byte, error) {
	return json.Marshal(o)
}
<commit_msg>[#ERNEST-525] : Service info to display nats IP and Fix nats<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package views

import (
	"encoding/json"
	"strings"

	"github.com/ernestio/api-gateway/models"
	"log"

	graph "gopkg.in/r3labs/graph.v2"
)

// ServiceRender : Service representation to be rendered on the frontend
type ServiceRender struct {
	ID              string              `json:"id"`
	DatacenterID    int                 `json:"datacenter_id"`
	Name            string              `json:"name"`
	Version         string              `json:"version"`
	Status          string              `json:"status"`
	UserID          int                 `json:"user_id"`
	UserName        string              `json:"user_name"`
	LastKnownError  string              `json:"last_known_error"`
	Options         string              `json:"options"`
	Definition      string              `json:"definition"`
	Vpcs            []map[string]string `json:"vpcs"`
	Networks        []map[string]string `json:"networks"`
	Instances       []map[string]string `json:"instances"`
	Nats            []map[string]string `json:"nats"`
	SecurityGroups  []map[string]string `json:"security_groups"`
	Elbs            []map[string]string `json:"elbs"`
	RDSClusters     []map[string]string `json:"rds_clusters"`
	RDSInstances    []map[string]string `json:"rds_instances"`
	EBSVolumes      []map[string]string `json:"ebs_volumes"`
	LoadBalancers   []map[string]string `json:"load_balancers"`
	SQLDatabases    []map[string]string `json:"sql_databases"`
	VirtualMachines []map[string]string `json:"virtual_machines"`
}

// Render : Map a Service to a ServiceRender
func (o *ServiceRender) Render(s models.Service) (err error) {
	o.ID = s.ID
	o.DatacenterID = s.DatacenterID
	o.Name = s.Name
	o.Version = s.Version.String()
	o.Status = s.Status
	o.UserID = s.UserID
	o.UserName = s.UserName
	if def, ok := s.Definition.(string); ok == true {
		o.Definition = def
	}

	g, err := s.Mapping()
	if err != nil {
		log.Println(err.Error())
		return err
	}

	o.Vpcs = RenderVpcs(g)
	o.Networks = RenderNetworks(g)
	o.SecurityGroups = RenderSecurityGroups(g)
	o.Nats = RenderNats(g)
	o.Instances = RenderInstances(g)
	o.Elbs = RenderELBs(g)
	o.RDSClusters = RenderRDSClusters(g)
	o.RDSInstances = RenderRDSInstances(g)
	o.EBSVolumes = RenderEBSVolumes(g)
	o.LoadBalancers = RenderLoadBalancers(g)
	o.SQLDatabases = RenderSQLDatabases(g)
	o.VirtualMachines = RenderVirtualMachines(g)

	return err
}

// RenderVpcs : renders a services vpcs
func RenderVpcs(g *graph.Graph) []map[string]string {
	var vpcs []map[string]string

	for _, n := range g.GetComponents().ByType("vpc") {
		gc := n.(*graph.GenericComponent)
		name, _ := (*gc)["name"].(string)
		id, _ := (*gc)["vpc_aws_id"].(string)
		subnet, _ := (*gc)["vpc_subnet"].(string)
		vpcs = append(vpcs, map[string]string{
			"name":       name,
			"vpc_id":     id,
			"vpc_subnet": subnet,
		})
	}

	return vpcs
}

// RenderNetworks : renders a services networks
func RenderNetworks(g *graph.Graph) []map[string]string {
	var networks []map[string]string

	for _, n := range g.GetComponents().ByType("network") {
		gc := n.(*graph.GenericComponent)
		name, _ := (*gc)["name"].(string)
		id, _ := (*gc)["network_aws_id"].(string)
		az, _ := (*gc)["availablity_zone"].(string)
		networks = append(networks, map[string]string{
			"name":              name,
			"network_aws_id":    id,
			"availability_zone": az,
		})
	}

	return networks
}

// RenderSecurityGroups : renders a services security groups
func RenderSecurityGroups(g *graph.Graph) []map[string]string {
	var sgs []map[string]string

	for _, n := range g.GetComponents().ByType("firewall") {
		gc := n.(*graph.GenericComponent)
		name, _ := (*gc)["name"].(string)
		id, _ := (*gc)["security_group_aws_id"].(string)
		sgs = append(sgs, map[string]string{
			"name":                  name,
			"security_group_aws_id": id,
		})
	}

	return sgs
}

// RenderNats : renders a services nat gateways
func RenderNats(g *graph.Graph) []map[string]string {
	var nats []map[string]string

	for _, n := range g.GetComponents().ByType("nat") {
		gc := n.(*graph.GenericComponent)
		name, _ := (*gc)["name"].(string)
		id, _ := (*gc)["nat_gateway_aws_id"].(string)
		pubIP, _ := (*gc)["nat_gateway_allocation_ip"].(string)
		nats = append(nats, map[string]string{
			"name":               name,
			"nat_gateway_aws_id": id,
			"public_ip":          pubIP,
		})
	}

	return nats
}

// RenderELBs : renders a services elbs
func RenderELBs(g *graph.Graph) []map[string]string {
	var elbs []map[string]string

	for _, n := range g.GetComponents().ByType("elb") {
		gc := n.(*graph.GenericComponent)
		name, _ := (*gc)["name"].(string)
		dns, _ := (*gc)["dns_name"].(string)
		elbs = append(elbs, map[string]string{
			"name":     name,
			"dns_name": dns,
		})
	}

	return elbs
}

// RenderInstances : renders a services instances
func RenderInstances(g *graph.Graph) []map[string]string {
	var instances []map[string]string

	for _, n := range g.GetComponents().ByType("instance") {
		gc := n.(*graph.GenericComponent)
		name, _ := (*gc)["name"].(string)
		id, _ := (*gc)["instance_aws_id"].(string)
		pip, _ := (*gc)["public_ip"].(string)
		ip, _ := (*gc)["ip"].(string)
		instances = append(instances, map[string]string{
			"name":            name,
			"instance_aws_id": id,
			"public_ip":       pip,
			"ip":              ip,
		})
	}

	return instances
}

// RenderRDSClusters : renders a services rds clusters
func RenderRDSClusters(g *graph.Graph) []map[string]string {
	var rdss []map[string]string

	for _, n := range g.GetComponents().ByType("rds_cluster") {
		gc := n.(*graph.GenericComponent)
		name, _ := (*gc)["name"].(string)
		endpoint, _ := (*gc)["endpoint"].(string)
		rdss = append(rdss, map[string]string{
			"name":     name,
			"endpoint": endpoint,
		})
	}

	return rdss
}

// RenderRDSInstances : renders a services rds instances
func RenderRDSInstances(g *graph.Graph) []map[string]string {
	var rdss []map[string]string

	for _, n := range g.GetComponents().ByType("rds_instance") {
		gc := n.(*graph.GenericComponent)
		name, _ := (*gc)["name"].(string)
		endpoint, _ := (*gc)["endpoint"].(string)
		rdss = append(rdss, map[string]string{
			"name":     name,
			"endpoint": endpoint,
		})
	}

	return rdss
}

// RenderEBSVolumes : renders a services ebs volumes
func RenderEBSVolumes(g *graph.Graph) []map[string]string {
	var rdss []map[string]string

	for _, n := range g.GetComponents().ByType("ebs_volume") {
		gc := n.(*graph.GenericComponent)
		name, _ := (*gc)["name"].(string)
		id, _ := (*gc)["volume_aws_id"].(string)
		rdss = append(rdss, map[string]string{
			"name":          name,
			"volume_aws_id": id,
		})
	}

	return rdss
}

// RenderLoadBalancers : renders load balancers
func RenderLoadBalancers(g *graph.Graph) []map[string]string {
	return renderResources(g, "lb", func(gc *graph.GenericComponent) map[string]string {
		name, _ := (*gc)["name"].(string)
		id, _ := (*gc)["id"].(string)
		configs, _ := (*gc)["frontend_ip_configurations"].([]interface{})
		cfg, _ := configs[0].(map[string]string)

		return map[string]string{
			"name":      name,
			"id":        id,
			"public_ip": cfg["public_ip_address"],
		}
	})
}

// RenderVirtualMachines : renders virtual machines
func RenderVirtualMachines(g *graph.Graph) []map[string]string {
	var resources []map[string]string
	mappedIPs := make(map[string]interface{}, 0)
	existingIPs := make(map[string]string, 0)

	for _, ip := range g.GetComponents().ByType("public_ip") {
		gc := ip.(*graph.GenericComponent)
		id, _ := (*gc)["id"].(string)
		ipAddress, _ := (*gc)["ip_address"].(string)
		existingIPs[id] = ipAddress
	}

	for _, ni := range g.GetComponents().ByType("network_interface") {
		var public []string
		var private []string

		gc := ni.(*graph.GenericComponent)
		name, _ := (*gc)["name"].(string)
		ips := make(map[string][]string)

		configs, _ := (*gc)["ip_configuration"].([]interface{})
		for _, cfg := range configs {
			c, _ := cfg.(map[string]interface{})
			pubID, _ := c["public_ip_address_id"].(string)
			pri, _ := c["private_ip_address"].(string)
			if pub, ok := existingIPs[pubID]; ok {
				public = append(public, pub)
			}
			private = append(private, pri)
		}

		ips["public"] = public
		ips["private"] = private
		mappedIPs[name] = make(map[string][]string, 0)
		mappedIPs[name] = ips
	}

	for _, n := range g.GetComponents().ByType("virtual_machine") {
		gc := n.(*graph.GenericComponent)
		name, _ := (*gc)["name"].(string)
		id, _ := (*gc)["id"].(string)
		networks, _ := (*gc)["network_interfaces"].([]interface{})
		publicIPs := make([]string, 0)
		privateIPs := make([]string, 0)
		for _, ni := range networks {
			netName := ni.(string)
			if val, ok := mappedIPs[netName]; ok {
				ips, _ := val.(map[string][]string)
				publicIPs = append(publicIPs, ips["public"]...)
				privateIPs = append(privateIPs, ips["private"]...)
			}
		}

		resources = append(resources, map[string]string{
			"name":       name,
			"id":         id,
			"public_ip":  strings.Join(publicIPs, ", "),
			"private_ip": strings.Join(privateIPs, ", "),
		})
	}

	return resources
}

// RenderSQLDatabases : renders sql databases
func RenderSQLDatabases(g *graph.Graph) []map[string]string {
	return renderResources(g, "sql_database", func(gc *graph.GenericComponent) map[string]string {
		name, _ := (*gc)["name"].(string)
		server, _ := (*gc)["server_name"].(string)
		id, _ := (*gc)["id"].(string)

		return map[string]string{
			"name":        name,
			"server_name": server,
			"id":          id,
		}
	})
}

type convert func(*graph.GenericComponent) map[string]string

func renderResources(g *graph.Graph, resourceType string, f convert) (resources []map[string]string) {
	for _, n := range g.GetComponents().ByType(resourceType) {
		gc := n.(*graph.GenericComponent)
		resources = append(resources, f(gc))
	}

	return
}

// RenderCollection : Maps a collection of Service on a collection of ServiceRender
func (o *ServiceRender) RenderCollection(services []models.Service) (list []ServiceRender, err error) {
	for _, s := range services {
		var output ServiceRender
		if err := output.Render(s); err == nil {
			list = append(list, output)
		}
	}

	return list, nil
}

// ToJSON : Converts a ServiceRender to json string
func (o *ServiceRender) ToJSON() ([]byte, error) {
	return json.Marshal(o)
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/binary"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"os"

	"golang.org/x/crypto/ssh"

	"github.com/docker/docker/pkg/mflag"
	"github.com/fsouza/go-dockerclient"
)

var (
	logger = log.New(os.Stdout, "", log.Ldate|log.Ltime)
)

func main() {

	config := struct {
		ListenAddr  string
		Port        uint
		KeyFile     string
		ShowHelp    bool
		ShowVersion bool
		Host        string
		Cmd         string
	}{}

	mflag.StringVar(&config.Host, []string{"H", "-host"}, "unix:///var/run/docker.sock", "docker host socket")
	mflag.StringVar(&config.ListenAddr, []string{"l", "-listen_addr"}, "0.0.0.0", "Listening Address")
	mflag.UintVar(&config.Port, []string{"p", "-port"}, 2232, "Listening Port")
	mflag.StringVar(&config.KeyFile, []string{"i", "-server_key"}, "/etc/ssh/ssh_host_rsa_key", "Key file for SSH")
	mflag.StringVar(&config.Cmd, []string{"c", "-command"}, "/bin/bash", "default exec command")
	mflag.BoolVar(&config.ShowHelp, []string{"h", "-help"}, false, "Print help and exit")
	mflag.BoolVar(&config.ShowVersion, []string{"-version"}, false, "Print version and exit")

	mflag.Parse()

	if config.ShowHelp {
		mflag.PrintDefaults()
		return
	}

	client, err := docker.NewClient(config.Host)
	if err != nil {
		logger.Fatalln("Cannot connect to docker %v", err)
	}

	server := &ssh.ServerConfig{
		PublicKeyCallback: func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) {
			// TODO query
			return nil, nil
		},
	}

	privateBytes, err := ioutil.ReadFile(config.KeyFile)
	if err != nil {
		logger.Fatalln(err)
	}

	private, err := ssh.ParsePrivateKey(privateBytes)
	if err != nil {
		logger.Fatalln(err)
	}

	server.AddHostKey(private)

	listener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", config.ListenAddr, config.Port))
	if err != nil {
		logger.Fatalln("failed to listen for connection: %v", err)
	}
	defer listener.Close()

	logger.Printf("Docker.sshd started")

	for {
		c, err := listener.Accept()
		if err != nil {
			logger.Printf("failed to accept connection: %v", err)
			continue
		}

		logger.Printf("connection accepted: %v", c.RemoteAddr())

		go func() {
			sshConn, chans, reqs, err := ssh.NewServerConn(c, server)

			if err != nil {
				logger.Printf("failed to establish ssh connection: %v", err)
				return
			}

			exec, err := client.CreateExec(docker.CreateExecOptions{
				Container:    sshConn.User(),
				AttachStdin:  true,
				AttachStdout: true,
				AttachStderr: true,
				Tty:          true,
				Cmd:          []string{config.Cmd},
			})

			if err != nil {
				logger.Printf("failed to create docker exec: %v", err)
				sshConn.Close()
				return
			}

			go handleRequests(reqs)
			// Accept all channels
			go handleChannels(client, exec.ID, chans)
		}()
	}
}

func handleRequests(reqs <-chan *ssh.Request) {
	for req := range reqs {
		if req.Type == "keepalive@openssh.com" {
			req.Reply(true, nil)
			continue
		}
		logger.Printf("recieved out-of-band request: %+v", req)
	}
}

func handleChannels(client *docker.Client, execID string, chans <-chan ssh.NewChannel) {
	// Service the incoming Channel channel.
	for newChannel := range chans {
		// Since we're handling the execution of a shell, we expect a
		// channel type of "session". However, there are also: "x11", "direct-tcpip"
		// and "forwarded-tcpip" channel types.
		if t := newChannel.ChannelType(); t != "session" {
			newChannel.Reject(ssh.UnknownChannelType, fmt.Sprintf("unknown channel type: %s", t))
			continue
		}

		// At this point, we have the opportunity to reject the client's
		// request for another logical connection
		channel, requests, err := newChannel.Accept()
		if err != nil {
			logger.Printf("could not accept channel (%s)", err)
			continue
		}
		// fire up bash for this session

		go func() {
			err := client.StartExec(execID, docker.StartExecOptions{
				Detach:       false,
				OutputStream: channel,
				ErrorStream:  channel,
				InputStream:  channel,
				RawTerminal:  false,
			})

			exit_status := 0

			if err != nil {
				exit_status = -1
			}

			channel.SendRequest("exit-status", false, ssh.Marshal(&struct{ uint32 }{uint32(exit_status)}))
			channel.Close()

			logger.Printf("session closed")
		}()

		// Sessions have out-of-band requests such as "shell", "pty-req" and "env"
		go func(in <-chan *ssh.Request) {
			for req := range in {
				ok := false
				switch req.Type {
				case "shell":
					// We don't accept any commands (Payload),
					// only the default shell.
					if len(req.Payload) == 0 {
						ok = true
					}
				case "pty-req":
					// Responding 'ok' here will let the client
					// know we have a pty ready for input
					ok = true
					// Parse body...
					termLen := req.Payload[3]
					termEnv := string(req.Payload[4 : termLen+4])
					h, w := parseDims(req.Payload[termLen+4:])
					client.ResizeExecTTY(execID, h, w)
					logger.Printf("pty-req '%s'", termEnv)
				case "window-change":
					h, w := parseDims(req.Payload)
					client.ResizeExecTTY(execID, h, w)
					logger.Printf("windows-changed %v, %v", h, w)
					continue //no response
				}
				if !ok {
					logger.Printf("declining %s request...", req.Type)
				}
				req.Reply(ok, nil)
			}
		}(requests)
	}
}

// parseDims extracts terminal dimensions (width x height) from the provided buffer.
func parseDims(b []byte) (int, int) {
	w := binary.BigEndian.Uint32(b)
	h := binary.BigEndian.Uint32(b[4:])
	return int(h), int(w)
}
<commit_msg>use ssh.Unmarshal to parse msg<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"os"

	"golang.org/x/crypto/ssh"

	"github.com/docker/docker/pkg/mflag"
	"github.com/fsouza/go-dockerclient"
)

var (
	logger = log.New(os.Stdout, "", log.Ldate|log.Ltime)
)

func main() {

	config := struct {
		ListenAddr  string
		Port        uint
		KeyFile     string
		ShowHelp    bool
		ShowVersion bool
		Host        string
		Cmd         string
	}{}

	mflag.StringVar(&config.Host, []string{"H", "-host"}, "unix:///var/run/docker.sock", "docker host socket")
	mflag.StringVar(&config.ListenAddr, []string{"l", "-listen_addr"}, "0.0.0.0", "Listening Address")
	mflag.UintVar(&config.Port, []string{"p", "-port"}, 2232, "Listening Port")
	mflag.StringVar(&config.KeyFile, []string{"i", "-server_key"}, "/etc/ssh/ssh_host_rsa_key", "Key file for SSH")
	mflag.StringVar(&config.Cmd, []string{"c", "-command"}, "/bin/bash", "default exec command")
	mflag.BoolVar(&config.ShowHelp, []string{"h", "-help"}, false, "Print help and exit")

	// TODO version
	mflag.BoolVar(&config.ShowVersion, []string{"-version"}, false, "Print version and exit")

	mflag.Parse()

	if config.ShowHelp {
		mflag.PrintDefaults()
		return
	}

	client, err := docker.NewClient(config.Host)
	if err != nil {
		logger.Fatalln("Cannot connect to docker %v", err)
	}

	server := &ssh.ServerConfig{
		PublicKeyCallback: func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) {
			// TODO query
			return nil, nil
		},
	}

	privateBytes, err := ioutil.ReadFile(config.KeyFile)
	if err != nil {
		logger.Fatalln(err)
	}

	private, err := ssh.ParsePrivateKey(privateBytes)
	if err != nil {
		logger.Fatalln(err)
	}

	server.AddHostKey(private)

	listener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", config.ListenAddr, config.Port))
	if err != nil {
		logger.Fatalln("failed to listen for connection: %v", err)
	}
	defer listener.Close()

	logger.Printf("Docker.sshd started")

	for {
		c, err := listener.Accept()
		if err != nil {
			logger.Printf("failed to accept connection: %v", err)
			continue
		}

		logger.Printf("connection accepted: %v", c.RemoteAddr())

		go func() {
			sshConn, chans, reqs, err := ssh.NewServerConn(c, server)

			if err != nil {
				logger.Printf("failed to establish ssh connection: %v", err)
				return
			}

			exec, err := client.CreateExec(docker.CreateExecOptions{
				Container:    sshConn.User(),
				AttachStdin:  true,
				AttachStdout: true,
				AttachStderr: true,
				Tty:          true,
				Cmd:          []string{config.Cmd},
			})

			if err != nil {
				logger.Printf("failed to create docker exec: %v", err)
				sshConn.Close()
				return
			}

			go handleRequests(reqs)
			// Accept all channels
			go handleChannels(client, exec.ID, chans)
		}()
	}
}

func handleRequests(reqs <-chan *ssh.Request) {
	for req := range reqs {
		if req.Type == "keepalive@openssh.com" {
			req.Reply(true, nil)
			continue
		}
		logger.Printf("recieved out-of-band request: %+v", req)
	}
}

func handleChannels(client *docker.Client, execID string, chans <-chan ssh.NewChannel) {
	// Service the incoming Channel channel.
	for newChannel := range chans {
		// Since we're handling the execution of a shell, we expect a
		// channel type of "session". However, there are also: "x11", "direct-tcpip"
		// and "forwarded-tcpip" channel types.
		if t := newChannel.ChannelType(); t != "session" {
			newChannel.Reject(ssh.UnknownChannelType, fmt.Sprintf("unknown channel type: %s", t))
			continue
		}

		// At this point, we have the opportunity to reject the client's
		// request for another logical connection
		channel, requests, err := newChannel.Accept()
		if err != nil {
			logger.Printf("could not accept channel (%s)", err)
			continue
		}

		go func() {
			err := client.StartExec(execID, docker.StartExecOptions{
				Detach:       false,
				OutputStream: channel,
				ErrorStream:  channel,
				InputStream:  channel,
				RawTerminal:  false,
			})

			// this call block until exec done
			exit_status := 0

			if err != nil {
				exit_status = -1
			}

			channel.SendRequest("exit-status", false, ssh.Marshal(&struct{ uint32 }{uint32(exit_status)}))
			channel.Close()

			logger.Printf("session closed")
		}()

		// Sessions have out-of-band requests such as "shell", "pty-req" and "env"
		// https://tools.ietf.org/html/rfc4254#
		// TODO impl more

		go func(in <-chan *ssh.Request) {
			for req := range in {
				ok := false

				switch req.Type {

				case "shell":
					if len(req.Payload) == 0 {
						ok = true
					}

				case "pty-req":
					// Responding 'ok' here will let the client
					// know we have a pty ready for input
					ok = true

					msg := struct {
						Term   string
						Width  uint32
						Height uint32
					}{}

					ssh.Unmarshal(req.Payload, &msg)

					client.ResizeExecTTY(execID, int(msg.Height), int(msg.Width))

					logger.Printf("pty-req '%v' %v * %v", msg.Term, msg.Height, msg.Width)

				case "window-change":

					msg := struct {
						Width  uint32
						Height uint32
					}{}

					ssh.Unmarshal(req.Payload, &msg)

					client.ResizeExecTTY(execID, int(msg.Height), int(msg.Width))

					logger.Printf("windows-changed %v * %v", msg.Height, msg.Width)

					// find way for this
					//case "env":

					//	msg := struct {
					//		Name  string
					//		Value string
					//	}{}

					//	ssh.Unmarshal(req.Payload, &msg)

					//    fmt.Println(msg)
				default:
					logger.Printf("unhandled req type %v", req.Type)
				}

				if req.WantReply {
					req.Reply(ok, nil)
				}
			}
		}(requests)
	}
}
<|endoftext|>