<commit_before>package main

import (
	"container/ring"
	"encoding/json"
	"encoding/xml"
	"fmt"
	"io/ioutil"
	"log"
	_ "net"
	"net/http"
	"regexp"
	"runtime"
	"time"
)

type RSSResult struct {
	XMLName xml.Name `xml:"rss"`
	Titles  []string `xml:"channel>item>title"`
}
type CacheItem struct {
	topic string
	ring  *ring.Ring
}

var cachemap = map[string]*CacheItem{
	"top":           &CacheItem{"", ring.New(10)},
	"world":         &CacheItem{"w", ring.New(10)},
	"US":            &CacheItem{"n", ring.New(10)},
	"business":      &CacheItem{"b", ring.New(10)},
	"technology":    &CacheItem{"t", ring.New(10)},
	"entertainment": &CacheItem{"e", ring.New(10)},
	"sports":        &CacheItem{"s", ring.New(10)},
	"science":       &CacheItem{"snc", ring.New(10)},
	"health":        &CacheItem{"m", ring.New(10)},
}
var cache = make([]byte, 1)

func check(e error) {
	if e != nil {
		panic(e)
	}
}

func get_news(key string, topic string) {
	rssurl := "https://news.google.com/news?pz=1&cf=all&ned=us&hl=en&output=rss&topic=" + topic
	log.Printf("Getting news to cachemap[%s] from %s\n", key, rssurl)
	r, _ := http.Get(rssurl)
	defer r.Body.Close()
	b, _ := ioutil.ReadAll(r.Body)
	//ioutil.WriteFile("news.xml", b, 0666) // write rss to disk
	c := regexp.MustCompile("(.+) - [^-]+$")
	//d, _ := xml.NewDecoder(r.Body)
	v := RSSResult{}
	_ = xml.Unmarshal(b, &v)
	for i, _ := range v.Titles { // reverse
		t := v.Titles[len(v.Titles)-1-i]
		st := c.FindAllStringSubmatch(t, -1)
		var s string
		if st == nil {
			s = t
		} else {
			s = st[0][1]
		}
		//fmt.Println(s)
		dup := false
		for _, v := range cachemap {
			v.ring.Do(func(o interface{}) { // check if already exists
				if dup || o == nil {
					return
				}
				if o == s {
					dup = true
				}
			})
		}
		if dup {
			fmt.Printf("%s is removed because of duplication\n", s)
			continue
		}
		cachemap[key].addRing(s)
		fmt.Println(s)
	}
}

func (cacheItem *CacheItem) addRing(value interface{}) {
	cacheItem.ring = cacheItem.ring.Prev()
	cacheItem.ring.Value = value
}

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU()) // use all physical cores

	http.HandleFunc("/news", newshandler)
	go http.ListenAndServe(":81", nil)

	for {
		log.Println("Refreshing cachemap")
		for k, v := range cachemap {
			get_news(k, v.topic)
		}
		update_cache()
		/* //debug purpose code
		for k, v := range cachemap {
			v.ring.Do(func(o interface{}) {
				fmt.Println("cache", k, ":", o)
			})
		} */
		time.Sleep(15 * time.Minute)
	}
}

func update_cache() {
	m := make(map[string][]string)
	for k, v := range cachemap {
		v.ring.Do(func(o interface{}) {
			if o != nil {
				m[k] = append(m[k], o.(string))
			}
		})
	}
	cache, _ = json.Marshal(m)
}

func newshandler(w http.ResponseWriter, r *http.Request) {
	ori := r.Header.Get("Origin")
	w.Header().Set("Access-Control-Allow-Origin", ori)
	w.Header().Set("Access-Control-Allow-Methods", "GET")
	//json.NewEncoder(w).Encode(m)
	log.Printf("%s is retrieving cache\n", r.RemoteAddr)
	w.Write(cache)
}
<commit_msg>Fix for error on offline<commit_after>package main

import (
	"container/ring"
	"encoding/json"
	"encoding/xml"
	"fmt"
	"io/ioutil"
	"log"
	_ "net"
	"net/http"
	"regexp"
	"runtime"
	"time"
)

type RSSResult struct {
	XMLName xml.Name `xml:"rss"`
	Titles  []string `xml:"channel>item>title"`
}
type CacheItem struct {
	topic string
	ring  *ring.Ring
}

var cachemap = map[string]*CacheItem{
	"top":           &CacheItem{"", ring.New(10)},
	"world":         &CacheItem{"w", ring.New(10)},
	"US":            &CacheItem{"n", ring.New(10)},
	"business":      &CacheItem{"b", ring.New(10)},
	"technology":    &CacheItem{"t", ring.New(10)},
	"entertainment": &CacheItem{"e", ring.New(10)},
	"sports":        &CacheItem{"s", ring.New(10)},
	"science":       &CacheItem{"snc", ring.New(10)},
	"health":        &CacheItem{"m", ring.New(10)},
}
var cache = make([]byte, 1) // final json string output cache

func check(e error) {
	if e != nil {
		panic(e)
	}
}

var title_company_filter = regexp.MustCompile("(.+) - [^-]+$") // regex to remove '- company' trail

func get_news(key string, topic string) bool {
	rssurl := "https://news.google.com/news?pz=1&cf=all&ned=us&hl=en&output=rss&topic=" + topic
	log.Printf("Getting news to cachemap[%s] from %s\n", key, rssurl)
	r, _ := http.Get(rssurl)
	if r == nil {
		log.Println("Unable to get news") // Probably the network is down
		return false
	}
	defer r.Body.Close()
	b, _ := ioutil.ReadAll(r.Body)
	//ioutil.WriteFile("news.xml", b, 0666) // write rss to disk
	//d, _ := xml.NewDecoder(r.Body)
	v := RSSResult{}
	_ = xml.Unmarshal(b, &v)     // Parse and filter to get titles only
	for i, _ := range v.Titles { // reverse
		t := v.Titles[len(v.Titles)-1-i]
		st := title_company_filter.FindAllStringSubmatch(t, -1) // filter out '- company' in title
		var s string
		if st == nil { // not filterable
			s = t
		} else { // filterable
			s = st[0][1]
		}
		//fmt.Println(s)
		dup := false
		for _, v := range cachemap {
			v.ring.Do(func(o interface{}) { // check if already exists
				if dup || o == nil {
					return
				}
				if o == s {
					dup = true
				}
			})
		}
		if dup {
			fmt.Printf("%s [duplicated and removed from buffer]\n", s)
			continue
		}
		cachemap[key].addRing(s)
		fmt.Println(s)
	}
	return true
}

func (cacheItem *CacheItem) addRing(value interface{}) {
	cacheItem.ring = cacheItem.ring.Prev()
	cacheItem.ring.Value = value
}

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU()) // use all physical cores

	http.HandleFunc("/news", newshandler)
	go http.ListenAndServe(":81", nil)

	for {
		log.Println("Refreshing cachemap")
		for k, v := range cachemap {
			get_news(k, v.topic)
		}
		update_cache()
		/* //debug purpose code
		for k, v := range cachemap {
			v.ring.Do(func(o interface{}) {
				fmt.Println("cache", k, ":", o)
			})
		} */
		time.Sleep(15 * time.Minute)
	}
}

func update_cache() {
	m := make(map[string][]string)
	for k, v := range cachemap {
		v.ring.Do(func(o interface{}) {
			if o != nil {
				m[k] = append(m[k], o.(string))
			}
		})
	}
	cache, _ = json.Marshal(m)
}

func newshandler(w http.ResponseWriter, r *http.Request) {
	ori := r.Header.Get("Origin")
	w.Header().Set("Access-Control-Allow-Origin", ori)
	w.Header().Set("Access-Control-Allow-Methods", "GET")
	//json.NewEncoder(w).Encode(m)
	log.Printf("%s is retrieving cache\n", r.RemoteAddr)
	w.Write(cache)
}
<|endoftext|>


<commit_before>package apifaker

import (
	. "github.com/Focinfi/gtester"
	"os"
	"testing"
)

var testRouter = &Router{apiFaker: &ApiFaker{}}

func TestNewModelWithPath(t *testing.T) {
	model, err := NewModelWithPath(testDir+"/users.json", testRouter)
	if err != nil {
		t.Error(err)
	}

	AssertEqual(t, model.Name, "users")
	AssertEqual(t, model.HasMany, []string{"books"})
}

func TestCheckRelationships(t *testing.T) {
	model, _ := NewModelWithPath(testDir+"/users.json", testRouter)
	model.HasMany = append(model.HasMany, model.HasMany...)
	AssertError(t, model.CheckRelationshipsMeta())
}

func TestCheckModelColumns(t *testing.T) {
	// has wrong columns count
	model, _ := NewModelWithPath(testDir+"/users.json", testRouter)
	model.Seeds[0] = map[string]interface{}{}
	if err := model.checkSeeds(); err == nil {
		t.Errorf("Can not detect columns count of seed item")
	}

	// has wrong column
	model, _ = NewModelWithPath(testDir+"/users.json", testRouter)
	model.Seeds[0] = map[string]interface{}{"foo0": "bar", "foo1": "bar", "foo2": "bar", "foo3": 1}
	if err := model.checkSeeds(); err == nil {
		t.Errorf("Can not check column name")
	}
}
func TestCheckColumnsTypes(t *testing.T) {
	model, _ := NewModelWithPath(testDir+"/users.json", testRouter)
	model.Columns[0].Type = "unsupportted_type"
	AssertError(t, model.checkColumnsMeta())
}

func TestCheckSeed(t *testing.T) {
	faker, _ := NewWithApiDir(testDir)
	userModel := faker.Routers["users"].Model
	bookModel := faker.Routers["books"].Model
	// type
	userModel.Seeds[0]["age"] = "22"
	AssertError(t, userModel.checkSeed(userModel.Seeds[0]))

	// regexp
	userModel.Seeds[1]["name"] = ",,,"
	AssertError(t, userModel.checkSeed(userModel.Seeds[1]))

	// uniqueness
	AssertError(t, userModel.checkSeed(userModel.Seeds[2]))

	// reset after delete
	firstLi, _ := userModel.Get(0)
	toDeleteName, _ := firstLi.Get("name")
	userModel.Delete(0)
	AssertEqual(t, userModel.Columns[0].CheckUniquenessOf(toDeleteName), true)

	// relationship
	book := map[string]interface{}{"id": float64(4), "title": "Animal Farm", "user_id": float64(100)}
	AssertError(t, bookModel.checkSeed(book))
	// t.Log(bookModel.checkSeed(book))
}

func TestSaveToFile(t *testing.T) {
	model, _ := NewModelWithPath(testDir+"/users.json", testRouter)
	liMap := map[string]interface{}{
		"id":    float64(4),
		"name":  "Monica",
		"phone": "12332132132",
		"age":   float64(21),
	}

	li := LineItem{liMap}
	model.Add(li)
	model.backfillSeeds()
	AssertEqual(t, len(model.Seeds), 4)
	AssertEqual(t, model.dataChanged, false)

	liInSeed, _ := model.Get(4)
	nameInSeed, _ := liInSeed.Get("name")
	AssertEqual(t, nameInSeed, liMap["name"])
	phoneInSeed, _ := liInSeed.Get("phone")
	AssertEqual(t, phoneInSeed, liMap["phone"])
	ageInSeed, _ := liInSeed.Get("age")
	AssertEqual(t, ageInSeed, liMap["age"])

	err := model.SaveToFile(testDir + "/users_temp.json")
	defer os.Remove(testDir + "/users_temp.json")

	if err != nil {
		t.Error(err.Error())
	} else {
		AssertEqual(t, model.dataChanged, false)
	}
}
<commit_msg>model_test: Update testing for SaveToFile<commit_after>package apifaker

import (
	. "github.com/Focinfi/gtester"
	"os"
	"testing"
)

var testRouter = &Router{apiFaker: &ApiFaker{}}

func TestNewModelWithPath(t *testing.T) {
	model, err := NewModelWithPath(testDir+"/users.json", testRouter)
	if err != nil {
		t.Error(err)
	}

	AssertEqual(t, model.Name, "users")
	AssertEqual(t, model.HasMany, []string{"books"})
}

func TestCheckRelationships(t *testing.T) {
	model, _ := NewModelWithPath(testDir+"/users.json", testRouter)
	model.HasMany = append(model.HasMany, model.HasMany...)
	AssertError(t, model.CheckRelationshipsMeta())
}

func TestCheckModelColumns(t *testing.T) {
	// has wrong columns count
	model, _ := NewModelWithPath(testDir+"/users.json", testRouter)
	model.Seeds[0] = map[string]interface{}{}
	if err := model.checkSeeds(); err == nil {
		t.Errorf("Can not detect columns count of seed item")
	}

	// has wrong column
	model, _ = NewModelWithPath(testDir+"/users.json", testRouter)
	model.Seeds[0] = map[string]interface{}{"foo0": "bar", "foo1": "bar", "foo2": "bar", "foo3": 1}
	if err := model.checkSeeds(); err == nil {
		t.Errorf("Can not check column name")
	}
}
func TestCheckColumnsTypes(t *testing.T) {
	model, _ := NewModelWithPath(testDir+"/users.json", testRouter)
	model.Columns[0].Type = "unsupportted_type"
	AssertError(t, model.checkColumnsMeta())
}

func TestCheckSeed(t *testing.T) {
	faker, _ := NewWithApiDir(testDir)
	userModel := faker.Routers["users"].Model
	bookModel := faker.Routers["books"].Model
	// type
	userModel.Seeds[0]["age"] = "22"
	AssertError(t, userModel.checkSeed(userModel.Seeds[0]))

	// regexp
	userModel.Seeds[1]["name"] = ",,,"
	AssertError(t, userModel.checkSeed(userModel.Seeds[1]))

	// uniqueness
	AssertError(t, userModel.checkSeed(userModel.Seeds[2]))

	// reset after delete
	firstLi, _ := userModel.Get(0)
	toDeleteName, _ := firstLi.Get("name")
	userModel.Delete(0)
	AssertEqual(t, userModel.Columns[0].CheckUniquenessOf(toDeleteName), true)

	// relationship
	book := map[string]interface{}{"id": float64(4), "title": "Animal Farm", "user_id": float64(100)}
	AssertError(t, bookModel.checkSeed(book))
	// t.Log(bookModel.checkSeed(book))
}

func TestSaveToFile(t *testing.T) {
	faker, _ := NewWithApiDir(testDir)
	model := faker.Routers["users"].Model
	liMap := map[string]interface{}{
		"id":    float64(4),
		"name":  "Monica",
		"phone": "12332132132",
		"age":   float64(21),
	}

	li := LineItem{liMap}
	e := model.Add(li)
	t.Log(e)
	model.backfillSeeds()
	AssertEqual(t, len(model.Seeds), 4)
	AssertEqual(t, model.dataChanged, false)

	liInSeed, _ := model.Get(4)
	nameInSeed, _ := liInSeed.Get("name")
	AssertEqual(t, nameInSeed, liMap["name"])
	phoneInSeed, _ := liInSeed.Get("phone")
	AssertEqual(t, phoneInSeed, liMap["phone"])
	ageInSeed, _ := liInSeed.Get("age")
	AssertEqual(t, ageInSeed, liMap["age"])

	err := model.SaveToFile(testDir + "/users_temp.json")
	defer os.Remove(testDir + "/users_temp.json")

	if err != nil {
		t.Error(err.Error())
	} else {
		AssertEqual(t, model.dataChanged, false)
	}
}
<|endoftext|>


<commit_before>package models

import (
  "github.com/spacedock-io/registry/db"
)

type Tag struct {
  Id         int64
  Tag        string `sql:"not null"`
  ImageId    int64
  Repo       string `sql:"not null"`
  Namespace  string `sql:"not null"`
}

func CreateTag(namespace string, repo string, tag string, uuid string) error {
  image := &Image{}
  q := db.DB.Where("Uuid = ?", uuid).Find(image)

  if q.RecordNotFound() {
    t := Tag{
      Tag: tag,
      Repo: repo,
      Namespace: namespace,
    }
    image.Tags = append(image.Tags, t)
    return image.Save()
  } else if q.Error != nil {
    return q.Error
  }

  return TagCreateErr
}

func GetTags(namespace string, repo string) ([]Tag, error) {
  var t []Tag
  q := db.DB.Where("Namespace = ? and Repo = ?", namespace, repo).Find(&t)
  if q.Error != nil {
    return nil, q.Error
  }
  return t, nil
}

func GetTag(namespace string, repo string, tag string) (*Tag, error) {
  t := &Tag{}
  q := db.DB.Where("Namespace = ? and Repo = ?", namespace, repo).Find(t)
  if q.Error != nil {
    return nil, q.Error
  }
  return t, nil
}

func (tag *Tag) Save() error {
  q := db.DB.Save(tag)
  return q.Error
}

func (tag *Tag) Create() error {
  return tag.Save()
}
<commit_msg>Use TagSaveErr in models.Tag.Save<commit_after>package models

import (
  "github.com/spacedock-io/registry/db"
)

type Tag struct {
  Id         int64
  Tag        string `sql:"not null"`
  ImageId    int64
  Repo       string `sql:"not null"`
  Namespace  string `sql:"not null"`
}

func CreateTag(namespace string, repo string, tag string, uuid string) error {
  image := &Image{}
  q := db.DB.Where("Uuid = ?", uuid).Find(image)

  if q.RecordNotFound() {
    t := Tag{
      Tag: tag,
      Repo: repo,
      Namespace: namespace,
    }
    image.Tags = append(image.Tags, t)
    return image.Save()
  } else if q.Error != nil {
    return q.Error
  }

  return TagCreateErr
}

func GetTags(namespace string, repo string) ([]Tag, error) {
  var t []Tag
  q := db.DB.Where("Namespace = ? and Repo = ?", namespace, repo).Find(&t)
  if q.Error != nil {
    return nil, q.Error
  }
  return t, nil
}

func GetTag(namespace string, repo string, tag string) (*Tag, error) {
  t := &Tag{}
  q := db.DB.Where("Namespace = ? and Repo = ?", namespace, repo).Find(t)
  if q.Error != nil {
    return nil, q.Error
  }
  return t, nil
}

func (tag *Tag) Save() error {
  q := db.DB.Save(tag)
  if q.Error != nil {
    return TagSaveErr
  }
  return nil
}

func (tag *Tag) Create() error {
  return tag.Save()
}
<|endoftext|>


<commit_before>package scipipe

import (
	"errors"
	"fmt"
	"os"
	"os/exec"
	str "strings"
)

// ================== ShellTask ==================

type ShellTask struct {
	InTargets     map[string]*FileTarget
	OutTargets    map[string]*FileTarget
	Params        map[string]string
	Command       string
	CustomExecute func(*ShellTask)
	Done          chan int
}

func NewShellTask(cmdPat string, inTargets map[string]*FileTarget, outPathFuncs map[string]func(*ShellTask) string, outPortsDoStream map[string]bool, params map[string]string, prepend string) *ShellTask {
	t := &ShellTask{
		InTargets:  inTargets,
		OutTargets: make(map[string]*FileTarget),
		Params:     params,
		Command:    "",
		Done:       make(chan int),
	}
	// Create out targets
	Debug.Printf("[ShellTask: %s] Creating outTargets now ...", cmdPat)
	outTargets := make(map[string]*FileTarget)
	for oname, ofun := range outPathFuncs {
		opath := ofun(t)
		otgt := NewFileTarget(opath)
		if outPortsDoStream[oname] {
			otgt.doStream = true
		}
		Debug.Printf("[ShellTask: %s] Creating outTarget with path %s ...", cmdPat, opath)
		outTargets[oname] = otgt
	}
	t.OutTargets = outTargets
	t.Command = formatCommand(cmdPat, inTargets, outTargets, params, prepend)
	Debug.Printf("[ShellTask: %s] Created formatted command: %s", cmdPat, t.Command)
	return t
}

// --------------- ShellTask API methods ----------------

func (t *ShellTask) GetInPath(inPort string) string {
	return t.InTargets[inPort].GetPath()
}

func (t *ShellTask) Execute() {
	defer close(t.Done)
	if !t.anyOutputExists() && !t.fifosInOutTargetsMissing() {
		if t.CustomExecute != nil {
			t.CustomExecute(t)
		} else {
			t.executeCommand(t.Command)
		}
		t.atomizeTargets()
	}
	Debug.Printf("[ShellTask: %s] Starting to send Done in t.Execute() ...)\n", t.Command)
	t.Done <- 1
	Debug.Printf("[ShellTask: %s] Done sending Done, in t.Execute()\n", t.Command)
}

// --------------- ShellTask Helper methods ----------------

// Check if any output file target, or temporary file targets, exist
func (t *ShellTask) anyOutputExists() (anyFileExists bool) {
	anyFileExists = false
	for _, tgt := range t.OutTargets {
		opath := tgt.GetPath()
		otmpPath := tgt.GetTempPath()
		if !tgt.doStream {
			if _, err := os.Stat(opath); err == nil {
				Warning.Printf("[ShellTask: %s] Output file already exists: %s. Check your workflow for correctness!\n", t.Command, opath)
				anyFileExists = true
			}
			if _, err := os.Stat(otmpPath); err == nil {
				Warning.Printf("[ShellTask: %s] Temporary Output file already exists: %s. Check your workflow for correctness!\n", t.Command, otmpPath)
				anyFileExists = true
			}
		}
	}
	return
}

// Check if any FIFO files for this tasks exist, for out-ports specified to support streaming
func (t *ShellTask) anyFifosExist() (anyFifosExist bool) {
	anyFifosExist = false
	for _, tgt := range t.OutTargets {
		ofifoPath := tgt.GetFifoPath()
		if tgt.doStream {
			if _, err := os.Stat(ofifoPath); err == nil {
				Warning.Printf("[ShellTask: %s] Output FIFO already exists: %s. Check your workflow for correctness!\n", t.Command, ofifoPath)
				anyFifosExist = true
			}
		}
	}
	return
}

// Make sure that FIFOs that are supposed to exist, really exists
func (t *ShellTask) fifosInOutTargetsMissing() (fifosInOutTargetsMissing bool) {
	fifosInOutTargetsMissing = false
	for _, tgt := range t.OutTargets {
		if tgt.doStream {
			ofifoPath := tgt.GetFifoPath()
			if _, err := os.Stat(ofifoPath); err != nil {
				Warning.Printf("[ShellTask: %s] FIFO Output file missing, for streaming output: %s. Check your workflow for correctness!\n", t.Command, ofifoPath)
				fifosInOutTargetsMissing = true
			}
		}
	}
	return
}

func (t *ShellTask) executeCommand(cmd string) {
	Info.Printf("[ShellTask: %s] Executing command: %s \n", t.Command, cmd)
	_, err := exec.Command("bash", "-c", cmd).Output()
	Check(err)
}

// Create FIFO files for all out-ports that are specified to support streaming
func (t *ShellTask) createFifos() {
	Debug.Printf("[ShellTask: %s] Now creating fifos for task\n", t.Command)
	for _, otgt := range t.OutTargets {
		if otgt.doStream {
			otgt.CreateFifo()
		}
	}
}

// Rename temporary output files to their proper file names
func (t *ShellTask) atomizeTargets() {
	for _, tgt := range t.OutTargets {
		if !tgt.doStream {
			Debug.Printf("Atomizing file: %s -> %s", tgt.GetTempPath(), tgt.GetPath())
			tgt.Atomize()
			Debug.Printf("Done atomizing file: %s -> %s", tgt.GetTempPath(), tgt.GetPath())
		} else {
			Debug.Printf("Target is streaming, so not atomizing: %s", tgt.GetPath())
		}
	}
}

// Clean up any remaining FIFOs
// TODO: this is actually not really used anymore ...
func (t *ShellTask) cleanUpFifos() {
	for _, tgt := range t.OutTargets {
		if tgt.doStream {
			Debug.Printf("[ShellTask: %s] Cleaning up FIFO for output target: %s\n", t.Command, tgt.GetFifoPath())
			tgt.RemoveFifo()
		} else {
			Debug.Printf("[ShellTask: %s] output target is not FIFO, so not removing any FIFO: %s\n", t.Command, tgt.GetPath())
		}
	}
}

// ================== Helper functions==================

func formatCommand(cmd string, inTargets map[string]*FileTarget, outTargets map[string]*FileTarget, params map[string]string, prepend string) string {

	// Debug.Println("Formatting command with the following data:")
	// Debug.Println("prepend:", prepend)
	// Debug.Println("cmd:", cmd)
	// Debug.Println("inTargets:", inTargets)
	// Debug.Println("outTargets:", outTargets)
	// Debug.Println("params:", params)

	r := getShellCommandPlaceHolderRegex()
	ms := r.FindAllStringSubmatch(cmd, -1)
	for _, m := range ms {
		placeHolderStr := m[0]
		typ := m[1]
		name := m[2]
		var filePath string
		if typ == "o" || typ == "os" {
			// Out-ports
			if outTargets[name] == nil {
				msg := fmt.Sprint("Missing outpath for outport '", name, "' for command '", cmd, "'")
				Check(errors.New(msg))
			} else {
				if typ == "o" {
					filePath = outTargets[name].GetTempPath() // Means important to Atomize afterwards!
				} else if typ == "os" {
					filePath = outTargets[name].GetFifoPath()
				}
			}
		} else if typ == "i" {
			// In-ports
			if inTargets[name] == nil {
				msg := fmt.Sprint("Missing intarget for inport '", name, "' for command '", cmd, "'")
				Check(errors.New(msg))
			} else if inTargets[name].GetPath() == "" {
				msg := fmt.Sprint("Missing inpath for inport '", name, "' for command '", cmd, "'")
				Check(errors.New(msg))
			} else {
				if inTargets[name].doStream {
					filePath = inTargets[name].GetFifoPath()
				} else {
					filePath = inTargets[name].GetPath()
				}
			}
		} else if typ == "p" {
			if params[name] == "" {
				msg := fmt.Sprint("Missing param value param '", name, "' for command '", cmd, "'")
				Check(errors.New(msg))
			} else {
				filePath = params[name]
			}
		}
		if filePath == "" {
			msg := fmt.Sprint("Replace failed for port ", name, " for command '", cmd, "'")
			Check(errors.New(msg))
		}
		cmd = str.Replace(cmd, placeHolderStr, filePath, -1)
	}
	// Add prepend string to the command
	if prepend != "" {
		cmd = fmt.Sprintf("%s %s", prepend, cmd)
	}
	return cmd
}
<commit_msg>Improve log messages on task execution, especially on custom Go code<commit_after>package scipipe

import (
	"errors"
	"fmt"
	"os"
	"os/exec"
	str "strings"
)

// ================== ShellTask ==================

type ShellTask struct {
	InTargets     map[string]*FileTarget
	OutTargets    map[string]*FileTarget
	Params        map[string]string
	Command       string
	CustomExecute func(*ShellTask)
	Done          chan int
}

func NewShellTask(cmdPat string, inTargets map[string]*FileTarget, outPathFuncs map[string]func(*ShellTask) string, outPortsDoStream map[string]bool, params map[string]string, prepend string) *ShellTask {
	t := &ShellTask{
		InTargets:  inTargets,
		OutTargets: make(map[string]*FileTarget),
		Params:     params,
		Command:    "",
		Done:       make(chan int),
	}
	// Create out targets
	Debug.Printf("[ShellTask: %s] Creating outTargets now ...", cmdPat)
	outTargets := make(map[string]*FileTarget)
	for oname, ofun := range outPathFuncs {
		opath := ofun(t)
		otgt := NewFileTarget(opath)
		if outPortsDoStream[oname] {
			otgt.doStream = true
		}
		Debug.Printf("[ShellTask: %s] Creating outTarget with path %s ...", cmdPat, opath)
		outTargets[oname] = otgt
	}
	t.OutTargets = outTargets
	t.Command = formatCommand(cmdPat, inTargets, outTargets, params, prepend)
	Debug.Printf("[ShellTask: %s] Created formatted command: %s", cmdPat, t.Command)
	return t
}

// --------------- ShellTask API methods ----------------

func (t *ShellTask) GetInPath(inPort string) string {
	return t.InTargets[inPort].GetPath()
}

func (t *ShellTask) Execute() {
	defer close(t.Done)
	if !t.anyOutputExists() && !t.fifosInOutTargetsMissing() {
		if t.CustomExecute != nil {
			Info.Printf("[Task: %s] Executing task.\n", t.Command)
			t.CustomExecute(t)
		} else {
			t.executeCommand(t.Command)
		}
		t.atomizeTargets()
	}
	Debug.Printf("[ShellTask: %s] Starting to send Done in t.Execute() ...)\n", t.Command)
	t.Done <- 1
	Debug.Printf("[ShellTask: %s] Done sending Done, in t.Execute()\n", t.Command)
}

// --------------- ShellTask Helper methods ----------------

// Check if any output file target, or temporary file targets, exist
func (t *ShellTask) anyOutputExists() (anyFileExists bool) {
	anyFileExists = false
	for _, tgt := range t.OutTargets {
		opath := tgt.GetPath()
		otmpPath := tgt.GetTempPath()
		if !tgt.doStream {
			if _, err := os.Stat(opath); err == nil {
				Warning.Printf("[ShellTask: %s] Output file already exists: %s, so skipping...\n", t.Command, opath)
				anyFileExists = true
			}
			if _, err := os.Stat(otmpPath); err == nil {
				Warning.Printf("[ShellTask: %s] Temporary Output file already exists: %s, so skipping...\n", t.Command, otmpPath)
				anyFileExists = true
			}
		}
	}
	return
}

// Check if any FIFO files for this tasks exist, for out-ports specified to support streaming
func (t *ShellTask) anyFifosExist() (anyFifosExist bool) {
	anyFifosExist = false
	for _, tgt := range t.OutTargets {
		ofifoPath := tgt.GetFifoPath()
		if tgt.doStream {
			if _, err := os.Stat(ofifoPath); err == nil {
				Warning.Printf("[ShellTask: %s] Output FIFO already exists: %s. Check your workflow for correctness!\n", t.Command, ofifoPath)
				anyFifosExist = true
			}
		}
	}
	return
}

// Make sure that FIFOs that are supposed to exist, really exists
func (t *ShellTask) fifosInOutTargetsMissing() (fifosInOutTargetsMissing bool) {
	fifosInOutTargetsMissing = false
	for _, tgt := range t.OutTargets {
		if tgt.doStream {
			ofifoPath := tgt.GetFifoPath()
			if _, err := os.Stat(ofifoPath); err != nil {
				Warning.Printf("[ShellTask: %s] FIFO Output file missing, for streaming output: %s. Check your workflow for correctness!\n", t.Command, ofifoPath)
				fifosInOutTargetsMissing = true
			}
		}
	}
	return
}

func (t *ShellTask) executeCommand(cmd string) {
	Info.Printf("[ShellTask: %s] Executing command: %s \n", t.Command, cmd)
	_, err := exec.Command("bash", "-c", cmd).Output()
	Check(err)
}

// Create FIFO files for all out-ports that are specified to support streaming
func (t *ShellTask) createFifos() {
	Debug.Printf("[ShellTask: %s] Now creating fifos for task\n", t.Command)
	for _, otgt := range t.OutTargets {
		if otgt.doStream {
			otgt.CreateFifo()
		}
	}
}

// Rename temporary output files to their proper file names
func (t *ShellTask) atomizeTargets() {
	for _, tgt := range t.OutTargets {
		if !tgt.doStream {
			Debug.Printf("Atomizing file: %s -> %s", tgt.GetTempPath(), tgt.GetPath())
			tgt.Atomize()
			Debug.Printf("Done atomizing file: %s -> %s", tgt.GetTempPath(), tgt.GetPath())
		} else {
			Debug.Printf("Target is streaming, so not atomizing: %s", tgt.GetPath())
		}
	}
}

// Clean up any remaining FIFOs
// TODO: this is actually not really used anymore ...
func (t *ShellTask) cleanUpFifos() {
	for _, tgt := range t.OutTargets {
		if tgt.doStream {
			Debug.Printf("[ShellTask: %s] Cleaning up FIFO for output target: %s\n", t.Command, tgt.GetFifoPath())
			tgt.RemoveFifo()
		} else {
			Debug.Printf("[ShellTask: %s] output target is not FIFO, so not removing any FIFO: %s\n", t.Command, tgt.GetPath())
		}
	}
}

// ================== Helper functions==================

func formatCommand(cmd string, inTargets map[string]*FileTarget, outTargets map[string]*FileTarget, params map[string]string, prepend string) string {

	// Debug.Println("Formatting command with the following data:")
	// Debug.Println("prepend:", prepend)
	// Debug.Println("cmd:", cmd)
	// Debug.Println("inTargets:", inTargets)
	// Debug.Println("outTargets:", outTargets)
	// Debug.Println("params:", params)

	r := getShellCommandPlaceHolderRegex()
	ms := r.FindAllStringSubmatch(cmd, -1)
	for _, m := range ms {
		placeHolderStr := m[0]
		typ := m[1]
		name := m[2]
		var filePath string
		if typ == "o" || typ == "os" {
			// Out-ports
			if outTargets[name] == nil {
				msg := fmt.Sprint("Missing outpath for outport '", name, "' for command '", cmd, "'")
				Check(errors.New(msg))
			} else {
				if typ == "o" {
					filePath = outTargets[name].GetTempPath() // Means important to Atomize afterwards!
				} else if typ == "os" {
					filePath = outTargets[name].GetFifoPath()
				}
			}
		} else if typ == "i" {
			// In-ports
			if inTargets[name] == nil {
				msg := fmt.Sprint("Missing intarget for inport '", name, "' for command '", cmd, "'")
				Check(errors.New(msg))
			} else if inTargets[name].GetPath() == "" {
				msg := fmt.Sprint("Missing inpath for inport '", name, "' for command '", cmd, "'")
				Check(errors.New(msg))
			} else {
				if inTargets[name].doStream {
					filePath = inTargets[name].GetFifoPath()
				} else {
					filePath = inTargets[name].GetPath()
				}
			}
		} else if typ == "p" {
			if params[name] == "" {
				msg := fmt.Sprint("Missing param value param '", name, "' for command '", cmd, "'")
				Check(errors.New(msg))
			} else {
				filePath = params[name]
			}
		}
		if filePath == "" {
			msg := fmt.Sprint("Replace failed for port ", name, " for command '", cmd, "'")
			Check(errors.New(msg))
		}
		cmd = str.Replace(cmd, placeHolderStr, filePath, -1)
	}
	// Add prepend string to the command
	if prepend != "" {
		cmd = fmt.Sprintf("%s %s", prepend, cmd)
	}
	return cmd
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	mp "github.com/mackerelio/go-mackerel-plugin"
	"net/http"
	"os"
)

var graphdef map[string](mp.Graphs) = map[string](mp.Graphs){
	"elasticsearch.http": mp.Graphs{
		Label: "Elasticsearch HTTP",
		Unit:  "integer",
		Metrics: [](mp.Metrics){
			mp.Metrics{Name: "http_opened", Label: "Opened", Diff: true},
		},
	},
	"elasticsearch.indices": mp.Graphs{
		Label: "Elasticsearch Indices",
		Unit:  "integer",
		Metrics: [](mp.Metrics){
			mp.Metrics{Name: "total_indexing_index", Label: "Indexing-Index", Diff: true, Stacked: true},
			mp.Metrics{Name: "total_indexing_delete", Label: "Indexing-Delete", Diff: true, Stacked: true},
			mp.Metrics{Name: "total_get", Label: "Get", Diff: true, Stacked: true},
			mp.Metrics{Name: "total_search_query", Label: "Search-Query", Diff: true, Stacked: true},
			mp.Metrics{Name: "total_search_fetch", Label: "Search-fetch", Diff: true, Stacked: true},
			mp.Metrics{Name: "total_merges", Label: "Merges", Diff: true, Stacked: true},
			mp.Metrics{Name: "total_refresh", Label: "Refresh", Diff: true, Stacked: true},
			mp.Metrics{Name: "total_flush", Label: "Flush", Diff: true, Stacked: true},
			mp.Metrics{Name: "total_warmer", Label: "Warmer", Diff: true, Stacked: true},
			mp.Metrics{Name: "total_percolate", Label: "Percolate", Diff: true, Stacked: true},
			mp.Metrics{Name: "total_suggest", Label: "Suggest", Diff: true, Stacked: true},
		},
	},
	"elasticsearch.indices.docs": mp.Graphs{
		Label: "Elasticsearch Indices Docs",
		Unit:  "integer",
		Metrics: [](mp.Metrics){
			mp.Metrics{Name: "docs_count", Label: "Count"},
			mp.Metrics{Name: "docs_deleted", Label: "Deleted"},
		},
	},
	"elasticsearch.indices.memory_size": mp.Graphs{
		Label: "Elasticsearch Indices Memory Size",
		Unit:  "bytes",
		Metrics: [](mp.Metrics){
			mp.Metrics{Name: "fielddata_size", Label: "Fielddata"},
			mp.Metrics{Name: "filter_cache_size", Label: "Filter Cache"},
		},
	},
	"elasticsearch.jvm.heap": mp.Graphs{
		Label: "Elasticsearch JVM Heap Mem",
		Unit:  "bytes",
		Metrics: [](mp.Metrics){
			mp.Metrics{Name: "heap_used", Label: "Used"},
			mp.Metrics{Name: "heap_max", Label: "Max"},
		},
	},
	"elasticsearch.thread_pool.threads": mp.Graphs{
		Label: "Elasticsearch Thread-Pool Threads",
		Unit:  "integer",
		Metrics: [](mp.Metrics){
			mp.Metrics{Name: "threads_generic", Label: "Generic", Stacked: true},
			mp.Metrics{Name: "threads_index", Label: "Index", Stacked: true},
			mp.Metrics{Name: "threads_snapshot_data", Label: "Snapshot Data", Stacked: true},
			mp.Metrics{Name: "threads_get", Label: "Get", Stacked: true},
			mp.Metrics{Name: "threads_bench", Label: "Bench", Stacked: true},
			mp.Metrics{Name: "threads_snapshot", Label: "Snapshot", Stacked: true},
			mp.Metrics{Name: "threads_merge", Label: "Merge", Stacked: true},
			mp.Metrics{Name: "threads_suggest", Label: "Suggest", Stacked: true},
			mp.Metrics{Name: "threads_bulk", Label: "Bulk", Stacked: true},
			mp.Metrics{Name: "threads_optimize", Label: "Optimize", Stacked: true},
			mp.Metrics{Name: "threads_warmer", Label: "Warmer", Stacked: true},
			mp.Metrics{Name: "threads_flush", Label: "Flush", Stacked: true},
			mp.Metrics{Name: "threads_search", Label: "Search", Stacked: true},
			mp.Metrics{Name: "threads_percolate", Label: "Percolate", Stacked: true},
			mp.Metrics{Name: "threads_refresh", Label: "Refresh", Stacked: true},
			mp.Metrics{Name: "threads_management", Label: "Management", Stacked: true},
		},
	},
	"elasticsearch.transport.count": mp.Graphs{
		Label: "Elasticsearch Transport Count",
		Unit:  "integer",
		Metrics: [](mp.Metrics){
			mp.Metrics{Name: "count_rx", Label: "TX", Diff: true},
			mp.Metrics{Name: "count_tx", Label: "RX", Diff: true},
		},
	},
}

var metricPlace map[string][]string = map[string][]string{
	"http_opened":           []string{"http", "total_opened"},
	"total_indexing_index":  []string{"indices", "indexing", "index_total"},
	"total_indexing_delete": []string{"indices", "indexing", "delete_total"},
	"total_get":             []string{"indices", "get", "total"},
	"total_search_query":    []string{"indices", "search", "query_total"},
	"total_search_fetch":    []string{"indices", "search", "fetch_total"},
	"total_merges":          []string{"indices", "merges", "total"},
	"total_refresh":         []string{"indices", "refresh", "total"},
	"total_flush":           []string{"indices", "flush", "total"},
	"total_warmer":          []string{"indices", "warmer", "total"},
	"total_percolate":       []string{"indices", "percolate", "total"},
	"total_suggest":         []string{"indices", "suggest", "total"},
	"docs_count":            []string{"indices", "docs", "count"},
	"docs_deleted":          []string{"indices", "docs", "deleted"},
	"fielddata_size":        []string{"indices", "fielddata", "memory_size_in_bytes"},
	"filter_cache_size":     []string{"indices", "filter_cache", "memory_size_in_bytes"},
	"heap_used":             []string{"jvm", "mem", "heap_used_in_bytes"},
	"heap_max":              []string{"jvm", "mem", "heap_max_in_bytes"},
	"threads_generic":       []string{"thread_pool", "generic", "threads"},
	"threads_index":         []string{"thread_pool", "index", "threads"},
	"threads_snapshot_data": []string{"thread_pool", "snapshot_data", "threads"},
	"threads_get":           []string{"thread_pool", "get", "threads"},
	"threads_bench":         []string{"thread_pool", "bench", "threads"},
	"threads_snapshot":      []string{"thread_pool", "snapshot", "threads"},
	"threads_merge":         []string{"thread_pool", "merge", "threads"},
	"threads_suggest":       []string{"thread_pool", "suggest", "threads"},
	"threads_bulk":          []string{"thread_pool", "bulk", "threads"},
	"threads_optimize":      []string{"thread_pool", "optimize", "threads"},
	"threads_warmer":        []string{"thread_pool", "warmer", "threads"},
	"threads_flush":         []string{"thread_pool", "flush", "threads"},
	"threads_search":        []string{"thread_pool", "search", "threads"},
	"threads_percolate":     []string{"thread_pool", "percolate", "threads"},
	"threads_refresh":       []string{"thread_pool", "refresh", "threads"},
	"threads_management":    []string{"thread_pool", "management", "threads"},
	"count_rx":              []string{"transport", "rx_count"},
	"count_tx":              []string{"transport", "tx_count"},
}

func GetFloatValue(s map[string]interface{}, keys []string) (float64, error) {
	var val float64
	sm := s
	for i, k := range keys {
		if i+1 < len(keys) {
			switch sm[k].(type) {
			case map[string]interface{}:
				sm = sm[k].(map[string]interface{})
			default:
				return 0, errors.New("Cannot handle as a hash")
			}
		} else {
			switch sm[k].(type) {
			case float64:
				val = sm[k].(float64)
			default:
				return 0, errors.New("Not float64")
			}
		}
	}

	return val, nil
}

type ElasticsearchPlugin struct {
	Uri string
}

func (p ElasticsearchPlugin) FetchMetrics() (map[string]float64, error) {
	resp, err := http.Get(p.Uri + "/_nodes/_local/stats")
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	stat := make(map[string]float64)
	decoder := json.NewDecoder(resp.Body)

	var s map[string]interface{}
	err = decoder.Decode(&s)
	if err != nil {
		return nil, err
	}

	nodes := s["nodes"].(map[string]interface{})
	n := ""
	for k, _ := range nodes {
		if n != "" {
			return nil, errors.New("Multiple node found")
		}
		n = k
	}
	node := nodes[n].(map[string]interface{})

	for k, v := range metricPlace {
		val, err := GetFloatValue(node, v)
		if err != nil {
			return nil, err
		}

		stat[k] = val
	}

	return stat, nil
}

func (n ElasticsearchPlugin) GraphDefinition() map[string](mp.Graphs) {
	return graphdef
}

func main() {
	optHost := flag.String("host", "localhost", "Host")
	optPort := flag.String("port", "9200", "Port")
	optTempfile := flag.String("tempfile", "", "Temp file name")
	flag.Parse()

	var elasticsearch ElasticsearchPlugin
	elasticsearch.Uri = fmt.Sprintf("http://%s:%s", *optHost, *optPort)

	helper := mp.NewMackerelPlugin(elasticsearch)
	if *optTempfile != "" {
		helper.Tempfile = *optTempfile
	} else {
		helper.Tempfile = fmt.Sprintf("/tmp/mackerel-plugin-elasticsearch-%s-%s", *optHost, *optPort)
	}

	if os.Getenv("MACKEREL_AGENT_PLUGIN_META") != "" {
		helper.OutputDefinitions()
	} else {
		helper.OutputValues()
	}
}
<commit_msg>not exit but warn when some values cannot be fetched<commit_after>package main

import (
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	mp "github.com/mackerelio/go-mackerel-plugin"
	"github.com/mackerelio/mackerel-agent/logging"
	"net/http"
	"os"
)

var logger = logging.GetLogger("metrics.plugin.postgres")

var graphdef map[string](mp.Graphs) = map[string](mp.Graphs){
	"elasticsearch.http": mp.Graphs{
		Label: "Elasticsearch HTTP",
		Unit:  "integer",
		Metrics: [](mp.Metrics){
			mp.Metrics{Name: "http_opened", Label: "Opened", Diff: true},
		},
	},
	"elasticsearch.indices": mp.Graphs{
		Label: "Elasticsearch Indices",
		Unit:  "integer",
		Metrics: [](mp.Metrics){
			mp.Metrics{Name: "total_indexing_index", Label: "Indexing-Index", Diff: true, Stacked: true},
			mp.Metrics{Name: "total_indexing_delete", Label: "Indexing-Delete", Diff: true, Stacked: true},
			mp.Metrics{Name: "total_get", Label: "Get", Diff: true, Stacked: true},
			mp.Metrics{Name: "total_search_query", Label: "Search-Query", Diff: true, Stacked: true},
			mp.Metrics{Name: "total_search_fetch", Label: "Search-fetch", Diff: true, Stacked: true},
			mp.Metrics{Name: "total_merges", Label: "Merges", Diff: true, Stacked: true},
			mp.Metrics{Name: "total_refresh", Label: "Refresh", Diff: true, Stacked: true},
			mp.Metrics{Name: "total_flush", Label: "Flush", Diff: true, Stacked: true},
			mp.Metrics{Name: "total_warmer", Label: "Warmer", Diff: true, Stacked: true},
			mp.Metrics{Name: "total_percolate", Label: "Percolate", Diff: true, Stacked: true},
			mp.Metrics{Name: "total_suggest", Label: "Suggest", Diff: true, Stacked: true},
		},
	},
	"elasticsearch.indices.docs": mp.Graphs{
		Label: "Elasticsearch Indices Docs",
		Unit:  "integer",
		Metrics: [](mp.Metrics){
			mp.Metrics{Name: "docs_count", Label: "Count"},
			mp.Metrics{Name: "docs_deleted", Label: "Deleted"},
		},
	},
	"elasticsearch.indices.memory_size": mp.Graphs{
		Label: "Elasticsearch Indices Memory Size",
		Unit:  "bytes",
		Metrics: [](mp.Metrics){
			mp.Metrics{Name: "fielddata_size", Label: "Fielddata"},
			mp.Metrics{Name: "filter_cache_size", Label: "Filter Cache"},
		},
	},
	"elasticsearch.jvm.heap": mp.Graphs{
		Label: "Elasticsearch JVM Heap Mem",
		Unit:  "bytes",
		Metrics: [](mp.Metrics){
			mp.Metrics{Name: "heap_used", Label: "Used"},
			mp.Metrics{Name: "heap_max", Label: "Max"},
		},
	},
	"elasticsearch.thread_pool.threads": mp.Graphs{
		Label: "Elasticsearch Thread-Pool Threads",
		Unit:  "integer",
		Metrics: [](mp.Metrics){
			mp.Metrics{Name: "threads_generic", Label: "Generic", Stacked: true},
			mp.Metrics{Name: "threads_index", Label: "Index", Stacked: true},
			mp.Metrics{Name: "threads_snapshot_data", Label: "Snapshot Data", Stacked: true},
			mp.Metrics{Name: "threads_get", Label: "Get", Stacked: true},
			mp.Metrics{Name: "threads_bench", Label: "Bench", Stacked: true},
			mp.Metrics{Name: "threads_snapshot", Label: "Snapshot", Stacked: true},
			mp.Metrics{Name: "threads_merge", Label: "Merge", Stacked: true},
			mp.Metrics{Name: "threads_suggest", Label: "Suggest", Stacked: true},
			mp.Metrics{Name: "threads_bulk", Label: "Bulk", Stacked: true},
			mp.Metrics{Name: "threads_optimize", Label: "Optimize", Stacked: true},
			mp.Metrics{Name: "threads_warmer", Label: "Warmer", Stacked: true},
			mp.Metrics{Name: "threads_flush", Label: "Flush", Stacked: true},
			mp.Metrics{Name: "threads_search", Label: "Search", Stacked: true},
			mp.Metrics{Name: "threads_percolate", Label: "Percolate", Stacked: true},
			mp.Metrics{Name: "threads_refresh", Label: "Refresh", Stacked: true},
			mp.Metrics{Name: "threads_management", Label: "Management", Stacked: true},
		},
	},
	"elasticsearch.transport.count": mp.Graphs{
		Label: "Elasticsearch Transport Count",
		Unit:  "integer",
		Metrics: [](mp.Metrics){
			mp.Metrics{Name: "count_rx", Label: "TX", Diff: true},
			mp.Metrics{Name: "count_tx", Label: "RX", Diff: true},
		},
	},
}

var metricPlace map[string][]string = map[string][]string{
	"http_opened":           []string{"http", "total_opened"},
	"total_indexing_index":  []string{"indices", "indexing", "index_total"},
	"total_indexing_delete": []string{"indices", "indexing", "delete_total"},
	"total_get":             []string{"indices", "get", "total"},
	"total_search_query":    []string{"indices", "search", "query_total"},
	"total_search_fetch":    []string{"indices", "search", "fetch_total"},
	"total_merges":          []string{"indices", "merges", "total"},
	"total_refresh":         []string{"indices", "refresh", "total"},
	"total_flush":           []string{"indices", "flush", "total"},
	"total_warmer":          []string{"indices", "warmer", "total"},
	"total_percolate":       []string{"indices", "percolate", "total"},
	"total_suggest":         []string{"indices", "suggest", "total"},
	"docs_count":            []string{"indices", "docs", "count"},
	"docs_deleted":          []string{"indices", "docs", "deleted"},
	"fielddata_size":        []string{"indices", "fielddata", "memory_size_in_bytes"},
	"filter_cache_size":     []string{"indices", "filter_cache", "memory_size_in_bytes"},
	"heap_used":             []string{"jvm", "mem", "heap_used_in_bytes"},
	"heap_max":              []string{"jvm", "mem", "heap_max_in_bytes"},
	"threads_generic":       []string{"thread_pool", "generic", "threads"},
	"threads_index":         []string{"thread_pool", "index", "threads"},
	"threads_snapshot_data": []string{"thread_pool", "snapshot_data", "threads"},
	"threads_get":           []string{"thread_pool", "get", "threads"},
	"threads_bench":         []string{"thread_pool", "bench", "threads"},
	"threads_snapshot":      []string{"thread_pool", "snapshot", "threads"},
	"threads_merge":         []string{"thread_pool", "merge", "threads"},
	"threads_suggest":       []string{"thread_pool", "suggest", "threads"},
	"threads_bulk":          []string{"thread_pool", "bulk", "threads"},
	"threads_optimize":      []string{"thread_pool", "optimize", "threads"},
	"threads_warmer":        []string{"thread_pool", "warmer", "threads"},
	"threads_flush":         []string{"thread_pool", "flush", "threads"},
	"threads_search":        []string{"thread_pool", "search", "threads"},
	"threads_percolate":     []string{"thread_pool", "percolate", "threads"},
	"threads_refresh":       []string{"thread_pool", "refresh", "threads"},
	"threads_management":    []string{"thread_pool", "management", "threads"},
	"count_rx":              []string{"transport", "rx_count"},
	"count_tx":              []string{"transport", "tx_count"},
}

func GetFloatValue(s map[string]interface{}, keys []string) (float64, error) {
	var val float64
	sm := s
	for i, k := range keys {
		if i+1 < len(keys) {
			switch sm[k].(type) {
			case map[string]interface{}:
				sm = sm[k].(map[string]interface{})
			default:
				return 0, errors.New("Cannot handle as a hash")
			}
		} else {
			switch sm[k].(type) {
			case float64:
				val = sm[k].(float64)
			default:
				return 0, errors.New("Not float64")
			}
		}
	}

	return val, nil
}

type ElasticsearchPlugin struct {
	Uri string
}

func (p ElasticsearchPlugin) FetchMetrics() (map[string]float64, error) {
	resp, err := http.Get(p.Uri + "/_nodes/_local/stats")
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	stat := make(map[string]float64)
	decoder := json.NewDecoder(resp.Body)

	var s map[string]interface{}
	err = decoder.Decode(&s)
	if err != nil {
		return nil, err
	}

	nodes := s["nodes"].(map[string]interface{})
	n := ""
	for k, _ := range nodes {
		if n != "" {
			return nil, errors.New("Multiple node found")
		}
		n = k
	}
	node := nodes[n].(map[string]interface{})

	for k, v := range metricPlace {
		val, err := GetFloatValue(node, v)
		if err != nil {
			logger.Errorf("Failed to find '%s': %s", k, err)
			continue
		}

		stat[k] = val
	}

	return stat, nil
}

func (n ElasticsearchPlugin) GraphDefinition() map[string](mp.Graphs) {
	return graphdef
}

func main() {
	optHost := flag.String("host", "localhost", "Host")
	optPort := flag.String("port", "9200", "Port")
	optTempfile := flag.String("tempfile", "", "Temp file name")
	flag.Parse()

	var elasticsearch ElasticsearchPlugin
	elasticsearch.Uri = fmt.Sprintf("http://%s:%s", *optHost, *optPort)

	helper := mp.NewMackerelPlugin(elasticsearch)
	if *optTempfile != "" {
		helper.Tempfile = *optTempfile
	} else {
		helper.Tempfile = fmt.Sprintf("/tmp/mackerel-plugin-elasticsearch-%s-%s", *optHost, *optPort)
	}

	if os.Getenv("MACKEREL_AGENT_PLUGIN_META") != "" {
		helper.OutputDefinitions()
	} else {
		helper.OutputValues()
	}
}
<|endoftext|>


<commit_before>/*
    PulseHA - HA Cluster Daemon
    Copyright (C) 2017  Andrew Zak <andrew@pulseha.com>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published
    by the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package main

import (
	"context"
	"github.com/Syleron/PulseHA/proto"
	"github.com/coreos/go-log/log"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"net"
	"os"
	"path/filepath"
	"sync"
	"time"
	"bytes"
	"encoding/binary"
)

// Note: Perhaps I need to consider splitting the CLI/CMD "server" and the main server into separate struct types.

/**
 * Server struct type
 */
type Server struct {
	sync.Mutex
	Status        proto.HealthCheckResponse_ServingStatus
	Last_response time.Time
	//Members []Member
	Config *Config
	Log log.Logger
	Server *grpc.Server
	Listener net.Listener
}

/**
 *
 */
func (s *Server) Check(ctx context.Context, in *proto.HealthCheckRequest) (*proto.HealthCheckResponse, error) {
	s.Lock()
	defer s.Unlock()
	switch in.Request {
	case proto.HealthCheckRequest_SETUP:
		log.Debug("Server:Check() - HealthCheckRequest Setup")
	case proto.HealthCheckRequest_STATUS:
		log.Debug("Server:Check() - HealthCheckRequest Status")
		return &proto.HealthCheckResponse{
			Status: proto.HealthCheckResponse_CONFIGURED,
		}, nil
	default:
	}
	return nil, nil
}

/**
 * Attempt to join a configured cluster
 */
func (s *Server) Join(ctx context.Context, in *proto.PulseJoin) (*proto.PulseJoin, error) {
	s.Lock()
	defer s.Unlock()
	log.Debug("Server:Join() - Join Pulse cluster")

	// Are we configured?
	if !clusterCheck(s.Config) {
		// Create a client
		client := &Client{}

		// Attempt to connect
		err := client.Connect(in.Ip, in.Port, in.Hostname)

		if err != nil {
			return &proto.PulseJoin{
				Success: false,
				Message: err.Error(),
			}, nil
		}

		newNode := Node{
			IP:       in.BindIp,
			Port:     in.BindPort,
			IPGroups: make(map[string][]string, 0),
		}

		// Convert struct into byte array
		buf := &bytes.Buffer{}
		err = binary.Write(buf, binary.BigEndian, newNode)
		if err != nil {
			log.Emergency(err)
		}

		//// Send our join request
		//r, err := client.SendJoin(&proto.PulseJoin{
		//	Replicated: true,
		//})

		// Set our config with the response

		// Close the connection
		client.Close()

		// This is called by our local daemon/agent
		// It needs to send a request to the peer/node to get cluster details.
		// Add the node to the config
		// Notify our peers that a new member has joined
		return &proto.PulseJoin{
			Success: true,
		}, nil
	}

	return &proto.PulseJoin{
		Success: false,
		Message: "Unable to join as PulseHA is already in a cluster.",
	}, nil
}

/**
 * Break cluster / Leave from cluster
 */
func (s *Server) Leave(ctx context.Context, in *proto.PulseLeave) (*proto.PulseLeave, error) {
	s.Lock()
	defer s.Unlock()
	log.Debug("Server:Leave() - Leave Pulse cluster")

	nodeTotal := len(s.Config.Nodes)

	// Are we even in a cluster?
	if nodeTotal == 0 {
		return &proto.PulseLeave{
			Success: false,
			Message: "Unable to leave as no cluster was found",
		}, nil
	}

	// Clear out the groups
	s.Config.Groups = map[string][]string{}
	// Clear out the nodes
	s.Config.Nodes = map[string]Node{}
	// save our config
	s.Config.Save()
	// Shutdown our main server
	s.Close()

	// Check to see if we are the only member in the cluster
	if nodeTotal == 1 {
		return &proto.PulseLeave{
			Success: true,
			Message: "Successfully dismantled cluster",
		}, nil
	}

	// We need to inform our peers that we have left!
	return &proto.PulseLeave{
		Success: true,
		Message: "Successfully left from cluster",
	}, nil
}

/**
 * Note: This will probably need to be replicated..
 */
func (s *Server) Create(ctx context.Context, in *proto.PulseCreate) (*proto.PulseCreate, error) {
	s.Lock()
	defer s.Unlock()
	log.Debug("Server:Create() - Create Pulse cluster")
	// Method of first checking to see if we are in a cluster.
	if !clusterCheck(s.Config) {
		// we are not in an active cluster
		newNode := Node{
			IP:       in.BindIp,
			Port:     in.BindPort,
			IPGroups: make(map[string][]string, 0),
		}
		// Add the node to the nodes config
		s.Config.Nodes[GetHostname()] = newNode
		// Assign interface names to node
		for _, ifaceName := range getInterfaceNames() {
			if ifaceName != "lo" {
				// Add the interface to the node
				newNode.IPGroups[ifaceName] = make([]string, 0)
				// Create a new group name
				groupName := genGroupName(s.Config)
				// Create a group for the interface
				s.Config.Groups[groupName] = []string{}
				// assign the group to the interface
				GroupAssign(groupName, GetHostname(), ifaceName, s.Config)
			}
		}
		// Save the config
		s.Config.Save()
		// Setup the listener
		go s.Setup()
		// return if we were successful or not
		return &proto.PulseCreate{
			Success: true,
			Message: "Pulse cluster successfully created!",
		}, nil
	} else {
		return &proto.PulseCreate{
			Success: false,
			Message: "Pulse daemon is already in a configured cluster",
		}, nil
	}
}

/**
 * Add a new floating IP group
 * Note: This will probably need to be replicated..
 */
func (s *Server) NewGroup(ctx context.Context, in *proto.PulseGroupNew) (*proto.PulseGroupNew, error) {
	s.Lock()
	defer s.Unlock()
	log.Debug("Server:NewGroup() - Create floating IP group")
	groupName, err := GroupNew(s.Config)
	if err != nil {
		return &proto.PulseGroupNew{
			Success: false,
			Message: err.Error(),
		}, nil
	}
	s.Config.Save()
	return &proto.PulseGroupNew{
		Success: true,
		Message: groupName + " successfully added.",
	}, nil
}

/**
 * Delete floating IP group
 */
func (s *Server) DeleteGroup(ctx context.Context, in *proto.PulseGroupDelete) (*proto.PulseGroupDelete, error) {
	s.Lock()
	defer s.Unlock()
	log.Debug("Server:DeleteGroup() - Delete floating IP group")
	err := GroupDelete(in.Name, s.Config)
	if err != nil {
		return &proto.PulseGroupDelete{
			Success: false,
			Message: err.Error(),
		}, nil
	}
	s.Config.Save()
	return &proto.PulseGroupDelete{
		Success: true,
		Message: in.Name + " successfully deleted.",
	}, nil
}

/**
 *
 * Note: This will probably need to be replicated..
 */
func (s *Server) GroupIPAdd(ctx context.Context, in *proto.PulseGroupAdd) (*proto.PulseGroupAdd, error) {
	s.Lock()
	defer s.Unlock()
	log.Debug("Server:GroupIPAdd() - Add IP addresses to group " + in.Name)
	err := GroupIpAdd(in.Name, in.Ips, s.Config)
	if err != nil {
		return &proto.PulseGroupAdd{
			Success: false,
			Message: err.Error(),
		}, nil
	}
	s.Config.Save()
	return &proto.PulseGroupAdd{
		Success: true,
		Message: "IP address(es) successfully added to " + in.Name,
	}, nil
}

/**
 *
 * Note: This will probably need to be replicated..
 */
func (s *Server) GroupIPRemove(ctx context.Context, in *proto.PulseGroupRemove) (*proto.PulseGroupRemove, error) {
	s.Lock()
	defer s.Unlock()
	log.Debug("Server:GroupIPRemove() - Removing IPs from group " + in.Name)
	err := GroupIpRemove(in.Name, in.Ips, s.Config)
	if err != nil {
		return &proto.PulseGroupRemove{
			Success: false,
			Message: err.Error(),
		}, nil
	}
	s.Config.Save()
	return &proto.PulseGroupRemove{
		Success: true,
		Message: "IP address(es) successfully removed from " + in.Name,
	}, nil
}

/**
 *
 * Note: This will probably need to be replicated..
 */
func (s *Server) GroupAssign(ctx context.Context, in *proto.PulseGroupAssign) (*proto.PulseGroupAssign, error) {
	s.Lock()
	defer s.Unlock()
	log.Debug("Server:GroupAssign() - Assigning group " + in.Group + " to interface " + in.Interface + " on node " + in.Node)
	err := GroupAssign(in.Group, in.Node, in.Interface, s.Config)
	if err != nil {
		return &proto.PulseGroupAssign{
			Success: false,
			Message: err.Error(),
		}, nil
	}
	s.Config.Save()
	return &proto.PulseGroupAssign{
		Success: true,
		Message: in.Group + " assigned to interface " + in.Interface + " on node " + in.Node,
	}, nil
}

/**
 *
 * Note: This will probably need to be replicated..
 */
func (s *Server) GroupUnassign(ctx context.Context, in *proto.PulseGroupUnassign) (*proto.PulseGroupUnassign, error) {
	s.Lock()
	defer s.Unlock()
	log.Debug("Server:GroupUnassign() - Unassigning group " + in.Group + " from interface " + in.Interface + " on node " + in.Node)
	err := GroupUnassign(in.Group, in.Node, in.Interface, s.Config)
	if err != nil {
		return &proto.PulseGroupUnassign{
			Success: false,
			Message: err.Error(),
		}, nil
	}
	s.Config.Save()
	return &proto.PulseGroupUnassign{
		Success: true,
		Message: in.Group + " unassigned from interface " + in.Interface + " on node " + in.Node,
	}, nil
}

/**
 *
 */
func (s *Server) GroupList(ctx context.Context, in *proto.PulseGroupList) (*proto.PulseGroupList, error) {
	s.Lock()
	defer s.Unlock()
	log.Debug("Server:GroupList() - Getting groups and their IPs")

	list := make(map[string]*proto.Group)

	for name, ips := range s.Config.Groups {
		list[name] = &proto.Group{
			Ips: ips,
		}
	}

	return &proto.PulseGroupList{
		Groups: list,
	}, nil
}

/**
 * Setup pulse cli type
 */
func (s *Server) SetupCLI() {
	lis, err := net.Listen("tcp", "127.0.0.1:9443")
	if err != nil {
		log.Errorf("Failed to listen: %s", err)
	}
	grpcServer := grpc.NewServer()
	proto.RegisterRequesterServer(grpcServer, s)
	log.Info("CLI initialised on 127.0.0.1:9443")
	grpcServer.Serve(lis)
}

/**
 * Setup pulse server type
 */
func (s *Server) Setup() {
	// Only continue if we are in a configured cluster
	if !clusterCheck(s.Config) {
		return
	}

	var err error
	s.Listener, err = net.Listen("tcp", s.Config.LocalNode().IP+":"+s.Config.LocalNode().Port)

	if err != nil {
		log.Errorf("Failed to listen: %s", err)
		os.Exit(1)
	}

	if s.Config.Pulse.TLS {
		// Get project directory location
		dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
		if err != nil {
			log.Emergency(err)
		}
		if CreateFolder(dir + "/certs") {
			log.Warning("TLS keys are missing! Generating..")
			GenOpenSSL()
		}

		creds, err := credentials.NewServerTLSFromFile(dir+"/certs/server.crt", dir+"/certs/server.key")

		if err != nil {
			log.Error("Could not load TLS keys.")
			os.Exit(1)
		}

		s.Server = grpc.NewServer(grpc.Creds(creds))
	} else {
		s.Server = grpc.NewServer()
	}

	proto.RegisterRequesterServer(s.Server, s)

	log.Info("Pulse initialised on " + s.Config.LocalNode().IP + ":" + s.Config.LocalNode().Port)

	s.Server.Serve(s.Listener)
}

/**
 * Shutdown pulse server (not cli/cmd)
 */
func (s *Server) Close() {
	log.Debug("Shutting down server")
	s.Server.GracefulStop()
	s.Listener.Close()
}
<commit_msg>Resolved variables names<commit_after>/*
    PulseHA - HA Cluster Daemon
    Copyright (C) 2017  Andrew Zak <andrew@pulseha.com>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published
    by the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package main

import (
	"context"
	"github.com/Syleron/PulseHA/proto"
	"github.com/coreos/go-log/log"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"net"
	"os"
	"path/filepath"
	"sync"
	"time"
	"bytes"
	"encoding/binary"
)

// Note: Perhaps I need to consider splitting the CLI/CMD "server" and the main server into separate struct types.

/**
 * Server struct type
 */
type Server struct {
	sync.Mutex
	Status        proto.HealthCheckResponse_ServingStatus
	Last_response time.Time
	//Members []Member
	Config *Config
	Log log.Logger
	Server *grpc.Server
	Listener net.Listener
}

/**
 *
 */
func (s *Server) Check(ctx context.Context, in *proto.HealthCheckRequest) (*proto.HealthCheckResponse, error) {
	s.Lock()
	defer s.Unlock()
	switch in.Request {
	case proto.HealthCheckRequest_SETUP:
		log.Debug("Server:Check() - HealthCheckRequest Setup")
	case proto.HealthCheckRequest_STATUS:
		log.Debug("Server:Check() - HealthCheckRequest Status")
		return &proto.HealthCheckResponse{
			Status: proto.HealthCheckResponse_CONFIGURED,
		}, nil
	default:
	}
	return nil, nil
}

/**
 * Attempt to join a configured cluster
 */
func (s *Server) Join(ctx context.Context, in *proto.PulseJoin) (*proto.PulseJoin, error) {
	s.Lock()
	defer s.Unlock()
	log.Debug("Server:Join() - Join Pulse cluster")

	// Are we configured?
	if !clusterCheck(s.Config) {
		// Create a client
		client := &Client{}

		// Attempt to connect
		err := client.Connect(in.Ip, in.Port, in.Hostname)

		if err != nil {
			return &proto.PulseJoin{
				Success: false,
				Message: err.Error(),
			}, nil
		}

		newNode := Node{
			IP:       in.Ip,
			Port:     in.Port,
			IPGroups: make(map[string][]string, 0),
		}

		// Convert struct into byte array
		buf := &bytes.Buffer{}
		err = binary.Write(buf, binary.BigEndian, newNode)
		if err != nil {
			log.Emergency(err)
		}

		//// Send our join request
		//r, err := client.SendJoin(&proto.PulseJoin{
		//	Replicated: true,
		//})

		// Set our config with the response

		// Close the connection
		client.Close()

		// This is called by our local daemon/agent
		// It needs to send a request to the peer/node to get cluster details.
		// Add the node to the config
		// Notify our peers that a new member has joined
		return &proto.PulseJoin{
			Success: true,
		}, nil
	}

	return &proto.PulseJoin{
		Success: false,
		Message: "Unable to join as PulseHA is already in a cluster.",
	}, nil
}

/**
 * Break cluster / Leave from cluster
 */
func (s *Server) Leave(ctx context.Context, in *proto.PulseLeave) (*proto.PulseLeave, error) {
	s.Lock()
	defer s.Unlock()
	log.Debug("Server:Leave() - Leave Pulse cluster")

	nodeTotal := len(s.Config.Nodes)

	// Are we even in a cluster?
	if nodeTotal == 0 {
		return &proto.PulseLeave{
			Success: false,
			Message: "Unable to leave as no cluster was found",
		}, nil
	}

	// Clear out the groups
	s.Config.Groups = map[string][]string{}
	// Clear out the nodes
	s.Config.Nodes = map[string]Node{}
	// save our config
	s.Config.Save()
	// Shutdown our main server
	s.Close()

	// Check to see if we are the only member in the cluster
	if nodeTotal == 1 {
		return &proto.PulseLeave{
			Success: true,
			Message: "Successfully dismantled cluster",
		}, nil
	}

	// We need to inform our peers that we have left!
	return &proto.PulseLeave{
		Success: true,
		Message: "Successfully left from cluster",
	}, nil
}

/**
 * Note: This will probably need to be replicated..
 */
func (s *Server) Create(ctx context.Context, in *proto.PulseCreate) (*proto.PulseCreate, error) {
	s.Lock()
	defer s.Unlock()
	log.Debug("Server:Create() - Create Pulse cluster")
	// Method of first checking to see if we are in a cluster.
	if !clusterCheck(s.Config) {
		// we are not in an active cluster
		newNode := Node{
			IP:       in.BindIp,
			Port:     in.BindPort,
			IPGroups: make(map[string][]string, 0),
		}
		// Add the node to the nodes config
		s.Config.Nodes[GetHostname()] = newNode
		// Assign interface names to node
		for _, ifaceName := range getInterfaceNames() {
			if ifaceName != "lo" {
				// Add the interface to the node
				newNode.IPGroups[ifaceName] = make([]string, 0)
				// Create a new group name
				groupName := genGroupName(s.Config)
				// Create a group for the interface
				s.Config.Groups[groupName] = []string{}
				// assign the group to the interface
				GroupAssign(groupName, GetHostname(), ifaceName, s.Config)
			}
		}
		// Save the config
		s.Config.Save()
		// Setup the listener
		go s.Setup()
		// return if we were successful or not
		return &proto.PulseCreate{
			Success: true,
			Message: "Pulse cluster successfully created!",
		}, nil
	} else {
		return &proto.PulseCreate{
			Success: false,
			Message: "Pulse daemon is already in a configured cluster",
		}, nil
	}
}

/**
 * Add a new floating IP group
 * Note: This will probably need to be replicated..
 */
func (s *Server) NewGroup(ctx context.Context, in *proto.PulseGroupNew) (*proto.PulseGroupNew, error) {
	s.Lock()
	defer s.Unlock()
	log.Debug("Server:NewGroup() - Create floating IP group")
	groupName, err := GroupNew(s.Config)
	if err != nil {
		return &proto.PulseGroupNew{
			Success: false,
			Message: err.Error(),
		}, nil
	}
	s.Config.Save()
	return &proto.PulseGroupNew{
		Success: true,
		Message: groupName + " successfully added.",
	}, nil
}

/**
 * Delete floating IP group
 */
func (s *Server) DeleteGroup(ctx context.Context, in *proto.PulseGroupDelete) (*proto.PulseGroupDelete, error) {
	s.Lock()
	defer s.Unlock()
	log.Debug("Server:DeleteGroup() - Delete floating IP group")
	err := GroupDelete(in.Name, s.Config)
	if err != nil {
		return &proto.PulseGroupDelete{
			Success: false,
			Message: err.Error(),
		}, nil
	}
	s.Config.Save()
	return &proto.PulseGroupDelete{
		Success: true,
		Message: in.Name + " successfully deleted.",
	}, nil
}

/**
 *
 * Note: This will probably need to be replicated..
 */
func (s *Server) GroupIPAdd(ctx context.Context, in *proto.PulseGroupAdd) (*proto.PulseGroupAdd, error) {
	s.Lock()
	defer s.Unlock()
	log.Debug("Server:GroupIPAdd() - Add IP addresses to group " + in.Name)
	err := GroupIpAdd(in.Name, in.Ips, s.Config)
	if err != nil {
		return &proto.PulseGroupAdd{
			Success: false,
			Message: err.Error(),
		}, nil
	}
	s.Config.Save()
	return &proto.PulseGroupAdd{
		Success: true,
		Message: "IP address(es) successfully added to " + in.Name,
	}, nil
}

/**
 *
 * Note: This will probably need to be replicated..
 */
func (s *Server) GroupIPRemove(ctx context.Context, in *proto.PulseGroupRemove) (*proto.PulseGroupRemove, error) {
	s.Lock()
	defer s.Unlock()
	log.Debug("Server:GroupIPRemove() - Removing IPs from group " + in.Name)
	err := GroupIpRemove(in.Name, in.Ips, s.Config)
	if err != nil {
		return &proto.PulseGroupRemove{
			Success: false,
			Message: err.Error(),
		}, nil
	}
	s.Config.Save()
	return &proto.PulseGroupRemove{
		Success: true,
		Message: "IP address(es) successfully removed from " + in.Name,
	}, nil
}

/**
 *
 * Note: This will probably need to be replicated..
 */
func (s *Server) GroupAssign(ctx context.Context, in *proto.PulseGroupAssign) (*proto.PulseGroupAssign, error) {
	s.Lock()
	defer s.Unlock()
	log.Debug("Server:GroupAssign() - Assigning group " + in.Group + " to interface " + in.Interface + " on node " + in.Node)
	err := GroupAssign(in.Group, in.Node, in.Interface, s.Config)
	if err != nil {
		return &proto.PulseGroupAssign{
			Success: false,
			Message: err.Error(),
		}, nil
	}
	s.Config.Save()
	return &proto.PulseGroupAssign{
		Success: true,
		Message: in.Group + " assigned to interface " + in.Interface + " on node " + in.Node,
	}, nil
}

/**
 *
 * Note: This will probably need to be replicated..
 */
func (s *Server) GroupUnassign(ctx context.Context, in *proto.PulseGroupUnassign) (*proto.PulseGroupUnassign, error) {
	s.Lock()
	defer s.Unlock()
	log.Debug("Server:GroupUnassign() - Unassigning group " + in.Group + " from interface " + in.Interface + " on node " + in.Node)
	err := GroupUnassign(in.Group, in.Node, in.Interface, s.Config)
	if err != nil {
		return &proto.PulseGroupUnassign{
			Success: false,
			Message: err.Error(),
		}, nil
	}
	s.Config.Save()
	return &proto.PulseGroupUnassign{
		Success: true,
		Message: in.Group + " unassigned from interface " + in.Interface + " on node " + in.Node,
	}, nil
}

/**
 *
 */
func (s *Server) GroupList(ctx context.Context, in *proto.PulseGroupList) (*proto.PulseGroupList, error) {
	s.Lock()
	defer s.Unlock()
	log.Debug("Server:GroupList() - Getting groups and their IPs")

	list := make(map[string]*proto.Group)

	for name, ips := range s.Config.Groups {
		list[name] = &proto.Group{
			Ips: ips,
		}
	}

	return &proto.PulseGroupList{
		Groups: list,
	}, nil
}

/**
 * Setup pulse cli type
 */
func (s *Server) SetupCLI() {
	lis, err := net.Listen("tcp", "127.0.0.1:9443")
	if err != nil {
		log.Errorf("Failed to listen: %s", err)
	}
	grpcServer := grpc.NewServer()
	proto.RegisterRequesterServer(grpcServer, s)
	log.Info("CLI initialised on 127.0.0.1:9443")
	grpcServer.Serve(lis)
}

/**
 * Setup pulse server type
 */
func (s *Server) Setup() {
	// Only continue if we are in a configured cluster
	if !clusterCheck(s.Config) {
		return
	}

	var err error
	s.Listener, err = net.Listen("tcp", s.Config.LocalNode().IP+":"+s.Config.LocalNode().Port)

	if err != nil {
		log.Errorf("Failed to listen: %s", err)
		os.Exit(1)
	}

	if s.Config.Pulse.TLS {
		// Get project directory location
		dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
		if err != nil {
			log.Emergency(err)
		}
		if CreateFolder(dir + "/certs") {
			log.Warning("TLS keys are missing! Generating..")
			GenOpenSSL()
		}

		creds, err := credentials.NewServerTLSFromFile(dir+"/certs/server.crt", dir+"/certs/server.key")

		if err != nil {
			log.Error("Could not load TLS keys.")
			os.Exit(1)
		}

		s.Server = grpc.NewServer(grpc.Creds(creds))
	} else {
		s.Server = grpc.NewServer()
	}

	proto.RegisterRequesterServer(s.Server, s)

	log.Info("Pulse initialised on " + s.Config.LocalNode().IP + ":" + s.Config.LocalNode().Port)

	s.Server.Serve(s.Listener)
}

/**
 * Shutdown pulse server (not cli/cmd)
 */
func (s *Server) Close() {
	log.Debug("Shutting down server")
	s.Server.GracefulStop()
	s.Listener.Close()
}
<|endoftext|>


<commit_before>package model

import (
	"fmt"
	"path"

	"github.com/syncthing/protocol"
	stmodel "github.com/syncthing/syncthing/lib/model"
	"github.com/syncthing/syncthing/lib/sync"
)

type Model struct {
	protoConn map[protocol.DeviceID]stmodel.Connection
	deviceVer map[protocol.DeviceID]string
	pmut      sync.RWMutex // protects protoConn and rawConn

	// TODO keep devices associated with each file
	entries     map[string]map[string]protocol.FileInfo
	childLookup map[string]map[string][]string
	fmut        sync.RWMutex // protects file information
}

func NewModel() *Model {
	return &Model{
		protoConn: make(map[protocol.DeviceID]stmodel.Connection),
		deviceVer: make(map[protocol.DeviceID]string),
		pmut:      sync.NewRWMutex(),

		entries:     make(map[string]map[string]protocol.FileInfo),
		childLookup: make(map[string]map[string][]string),
		fmut:        sync.NewRWMutex(),
	}
}

func (m *Model) AddConnection(conn stmodel.Connection) {
	deviceID := conn.ID()

	m.pmut.Lock()

	if _, ok := m.protoConn[deviceID]; ok {
		panic("add existing device")
	}
	m.protoConn[deviceID] = conn

	conn.Start()

	/* send cluster config */ // TODO stop hard coding this, get it from model, like syncthing?
	cm := protocol.ClusterConfigMessage{
		// TODO set these correctly
		ClientName:    "Syncthing-FUSE",
		ClientVersion: "0.0.0",
		Options:       []protocol.Option{},
	}
	cr := protocol.Folder{
		ID: "default",
	}
	cm.Folders = append(cm.Folders, cr)
	conn.ClusterConfig(cm)

	m.pmut.Unlock()
}

func (m *Model) ConnectedTo(deviceID protocol.DeviceID) bool {
	m.pmut.RLock()
	_, ok := m.protoConn[deviceID]
	m.pmut.RUnlock()
	return ok
}

func (m *Model) IsPaused(deviceID protocol.DeviceID) bool {
	return false
}

func (m *Model) GetEntry(folder string, path string) protocol.FileInfo {
	m.fmut.RLock()

	entry := m.entries[folder][path]

	m.fmut.RUnlock()

	return entry
}

func (m *Model) GetFileData(folder string, path string) ([]byte, error) {
	m.fmut.RLock()

	entry := m.entries[folder][path]
	if debug {
		l.Debugln("Creating data for", folder, path, "size", entry.Size())
	}
	data := make([]byte, entry.Size())

	for _, conn := range m.protoConn {
		for i, block := range entry.Blocks {
			if debug {
				l.Debugln("Fetching block", i, "size", block.Size)
			}
			byteOffset := int64(i * protocol.BlockSize)
			flags := uint32(0)
			blockData, err := conn.Request(folder, path, byteOffset, int(block.Size), block.Hash, flags, []protocol.Option{})
			if err != nil {
				return blockData, err
			}
			// TODO check hash
			if debug {
				l.Debugln("Putting data at", byteOffset)
			}
			for j, k := byteOffset, int32(0); k < block.Size; j, k = j+1, k+1 {
				data[j] = blockData[k]
			}
		}

		break // only one device for now ...
		// TODO support multiple devices
		// TODO support zero devices
	}

	m.fmut.RUnlock()

	return data, nil
}

func (m *Model) GetChildren(folder string, path string) []protocol.FileInfo {
	m.fmut.RLock()

	// TODO assert is directory?

	entries := m.childLookup[folder][path]
	result := make([]protocol.FileInfo, len(entries))
	for i, childPath := range entries {
		result[i] = m.entries[folder][childPath]
	}

	m.fmut.RUnlock()

	return result
}

// An index was received from the peer device
func (m *Model) Index(deviceID protocol.DeviceID, folder string, files []protocol.FileInfo, flags uint32, options []protocol.Option) {
	if debug {
		l.Debugln("model: receiving index from device", deviceID, "for folder", folder)
	}

	m.fmut.Lock()

	_, ok := m.entries[folder]
	if !ok {
		m.entries[folder] = make(map[string]protocol.FileInfo)
		m.childLookup[folder] = make(map[string][]string)
	}

	for _, file := range files {
		if file.IsDeleted() {
			l.Debugln("model Index: peer has deleted file", file.Name)
			continue
		}
		if file.IsInvalid() {
			l.Debugln("model Index: peer has invalid file", file.Name)
			continue
		}
		if file.IsSymlink() {
			l.Debugln("model Index: peer has symlink", file.Name)
			continue
		}
		l.Debugln("model Index: peer has file/dir", file.Name)

		// Add to primary lookup
		m.entries[folder][file.Name] = file

		// add to directory children lookup
		dir := path.Dir(file.Name)
		_, ok := m.childLookup[folder][dir]
		if ok {
			m.childLookup[folder][dir] = append(m.childLookup[folder][dir], file.Name)
		} else {
			children := make([]string, 1)
			children[0] = file.Name
			m.childLookup[folder][dir] = children
		}
	}

	m.fmut.Unlock()
}

// An index update was received from the peer device
func (m *Model) IndexUpdate(deviceID protocol.DeviceID, folder string, files []protocol.FileInfo, flags uint32, options []protocol.Option) {
}

// A request was made by the peer device
func (m *Model) Request(deviceID protocol.DeviceID, folder string, name string, offset int64, hash []byte, flags uint32, options []protocol.Option, buf []byte) error {
	return protocol.ErrNoSuchFile
}

// A cluster configuration message was received
func (m *Model) ClusterConfig(deviceID protocol.DeviceID, config protocol.ClusterConfigMessage) {
	fmt.Println("model: receiving cluster config from device ", deviceID)
}

// The peer device closed the connection
func (m *Model) Close(deviceID protocol.DeviceID, err error) {
}
<commit_msg>Categorize logging in model<commit_after>package model

import (
	"fmt"
	"path"

	"github.com/syncthing/protocol"
	stmodel "github.com/syncthing/syncthing/lib/model"
	"github.com/syncthing/syncthing/lib/sync"
)

type Model struct {
	protoConn map[protocol.DeviceID]stmodel.Connection
	deviceVer map[protocol.DeviceID]string
	pmut      sync.RWMutex // protects protoConn and rawConn

	// TODO keep devices associated with each file
	entries     map[string]map[string]protocol.FileInfo
	childLookup map[string]map[string][]string
	fmut        sync.RWMutex // protects file information
}

func NewModel() *Model {
	return &Model{
		protoConn: make(map[protocol.DeviceID]stmodel.Connection),
		deviceVer: make(map[protocol.DeviceID]string),
		pmut:      sync.NewRWMutex(),

		entries:     make(map[string]map[string]protocol.FileInfo),
		childLookup: make(map[string]map[string][]string),
		fmut:        sync.NewRWMutex(),
	}
}

func (m *Model) AddConnection(conn stmodel.Connection) {
	deviceID := conn.ID()

	m.pmut.Lock()

	if _, ok := m.protoConn[deviceID]; ok {
		panic("add existing device")
	}
	m.protoConn[deviceID] = conn

	conn.Start()

	/* send cluster config */ // TODO stop hard coding this, get it from model, like syncthing?
	cm := protocol.ClusterConfigMessage{
		// TODO set these correctly
		ClientName:    "Syncthing-FUSE",
		ClientVersion: "0.0.0",
		Options:       []protocol.Option{},
	}
	cr := protocol.Folder{
		ID: "default",
	}
	cm.Folders = append(cm.Folders, cr)
	conn.ClusterConfig(cm)

	m.pmut.Unlock()
}

func (m *Model) ConnectedTo(deviceID protocol.DeviceID) bool {
	m.pmut.RLock()
	_, ok := m.protoConn[deviceID]
	m.pmut.RUnlock()
	return ok
}

func (m *Model) IsPaused(deviceID protocol.DeviceID) bool {
	return false
}

func (m *Model) GetEntry(folder string, path string) protocol.FileInfo {
	m.fmut.RLock()

	entry := m.entries[folder][path]

	m.fmut.RUnlock()

	return entry
}

func (m *Model) GetFileData(folder string, path string) ([]byte, error) {
	m.fmut.RLock()

	entry := m.entries[folder][path]
	if debug {
		l.Debugln("Creating data for", folder, path, "size", entry.Size())
	}
	data := make([]byte, entry.Size())

	for _, conn := range m.protoConn {
		for i, block := range entry.Blocks {
			if debug {
				l.Debugln("Fetching block", i, "size", block.Size)
			}
			byteOffset := int64(i * protocol.BlockSize)
			flags := uint32(0)
			blockData, err := conn.Request(folder, path, byteOffset, int(block.Size), block.Hash, flags, []protocol.Option{})
			if err != nil {
				return blockData, err
			}
			// TODO check hash
			if debug {
				l.Debugln("Putting data at", byteOffset)
			}
			for j, k := byteOffset, int32(0); k < block.Size; j, k = j+1, k+1 {
				data[j] = blockData[k]
			}
		}

		break // only one device for now ...
		// TODO support multiple devices
		// TODO support zero devices
	}

	m.fmut.RUnlock()

	return data, nil
}

func (m *Model) GetChildren(folder string, path string) []protocol.FileInfo {
	m.fmut.RLock()

	// TODO assert is directory?

	entries := m.childLookup[folder][path]
	result := make([]protocol.FileInfo, len(entries))
	for i, childPath := range entries {
		result[i] = m.entries[folder][childPath]
	}

	m.fmut.RUnlock()

	return result
}

// An index was received from the peer device
func (m *Model) Index(deviceID protocol.DeviceID, folder string, files []protocol.FileInfo, flags uint32, options []protocol.Option) {
	if debug {
		l.Debugln("model: receiving index from device", deviceID, "for folder", folder)
	}

	m.fmut.Lock()

	_, ok := m.entries[folder]
	if !ok {
		m.entries[folder] = make(map[string]protocol.FileInfo)
		m.childLookup[folder] = make(map[string][]string)
	}

	for _, file := range files {
		if file.IsDeleted() {
			if debug {
				l.Debugln("model Index: peer has deleted file", file.Name)
			}
			continue
		}
		if file.IsInvalid() {
			if debug {
				l.Debugln("model Index: peer has invalid file", file.Name)
			}
			continue
		}
		if file.IsSymlink() {
			if debug {
				l.Debugln("model Index: peer has symlink", file.Name)
			}
			continue
		}
		if debug {
			l.Debugln("model Index: peer has file/dir", file.Name)
		}

		// Add to primary lookup
		m.entries[folder][file.Name] = file

		// add to directory children lookup
		dir := path.Dir(file.Name)
		_, ok := m.childLookup[folder][dir]
		if ok {
			m.childLookup[folder][dir] = append(m.childLookup[folder][dir], file.Name)
		} else {
			children := make([]string, 1)
			children[0] = file.Name
			m.childLookup[folder][dir] = children
		}
	}

	m.fmut.Unlock()
}

// An index update was received from the peer device
func (m *Model) IndexUpdate(deviceID protocol.DeviceID, folder string, files []protocol.FileInfo, flags uint32, options []protocol.Option) {
}

// A request was made by the peer device
func (m *Model) Request(deviceID protocol.DeviceID, folder string, name string, offset int64, hash []byte, flags uint32, options []protocol.Option, buf []byte) error {
	return protocol.ErrNoSuchFile
}

// A cluster configuration message was received
func (m *Model) ClusterConfig(deviceID protocol.DeviceID, config protocol.ClusterConfigMessage) {
	fmt.Println("model: receiving cluster config from device ", deviceID)
}

// The peer device closed the connection
func (m *Model) Close(deviceID protocol.DeviceID, err error) {
}
<|endoftext|>


<commit_before>package money_test

import (
	"fmt"
	"testing"

	"github.com/fgrid/money"
)

func ExampleNew() {
	m := money.New(123, money.NewCurrency("USD"))
	fmt.Println(m)
	// Output: USD 1.23
}

func ExampleMoney_Cents() {
	fmt.Println(money.AUD(1).Cents())
	// Output: 1
}

func ExampleMoney_String() {
	fmt.Println(money.EUR(1))
	// Output: EUR 0.01
}

func ExampleMoney_String_second() {
	fmt.Println(money.EUR(100))
	// Output: EUR 1.00
}

func ExampleMoney_String_third() {
	fmt.Println(money.EUR(100).Debit())
	// Output: EUR -1.00
}

func ExampleMoney_Add() {
	m1 := money.EUR(1)
	m2 := money.EUR(100)
	m3, _ := m1.Add(m2)
	fmt.Println(m3)
	// Output: EUR 1.01
}

func ExampleMoney_Add_second() {
	m1 := money.EUR(1).Debit()
	m2, _ := m1.Add(m1)
	fmt.Println(m2)
	// Output: EUR -0.02
}

func ExampleMoney_Add_third() {
	m1 := money.EUR(1)
	m2 := money.USD(1)
	_, err := m1.Add(m2)
	fmt.Println(err.Error())
	// Output: different currencies not allowed
}

func ExampleMoney_Sub() {
	m1 := money.GBP(1)
	m2 := money.GBP(100)

	m3, _ := m1.Sub(m2)
	m4, _ := m2.Sub(m1)

	fmt.Println(m3)
	fmt.Println(m4)
	// Output:
	// GBP -0.99
	// GBP 0.99
}

func ExampleMoney_Inv() {
	m1 := money.CHF(1)
	m2 := m1.Inv()
	m3 := m2.Inv()

	fmt.Println(m1)
	fmt.Println(m2)
	fmt.Println(m3)

	// Output:
	// CHF 0.01
	// CHF -0.01
	// CHF 0.01
}

func ExampleMoney_Mul() {
	m1 := money.JPY(51)
	m2, _ := m1.Mul(3)

	fmt.Println(m2)
	// Output: JPY 1.53
}

func ExampleMoney_Percent() {
	m1 := money.CNY(100)
	m2, _ := m1.Percent(33)

	fmt.Println(m2)
	// Output: CNY 0.33
}

func ExampleMoney_Div_rounding_up() {
	u1, _ := money.EUR(105).Div(10)
	u2, _ := money.EUR(106).Div(10)

	fmt.Println(u1)
	fmt.Println(u2)
	// Output:
	// EUR 0.11
	// EUR 0.11
}

func ExampleMoney_Div_rounding_down() {
	d1, _ := money.EUR(104).Div(10)
	d2, _ := money.EUR(100).Div(10)

	fmt.Println(d1)
	fmt.Println(d2)
	// Output:
	// EUR 0.10
	// EUR 0.10
}

func ExampleMoney_Div_rounding_half_away_from_zero() {
	d1, _ := money.EUR(105).Debit().Div(10)
	u2, _ := money.EUR(105).Div(10)

	fmt.Println(d1)
	fmt.Println(u2)
	// Output:
	// EUR -0.11
	// EUR 0.11
}

func TestMoneyEquals(t *testing.T) {
	m1 := money.INR(101)
	m2, _ := m1.Add(money.INR(1))
	m3, _ := m2.Sub(money.INR(1))

	if !m1.Equals(m3) {
		t.Error("failed equality check")
	}
}

func TestMoneyEqualsDebits(t *testing.T) {
	m1 := money.RUB(101).Debit()
	m2 := money.RUB(101).Credit()

	if m1.Equals(m2) {
		t.Error("failed equality check by debits")
	}
}

func TestMoneyNilEqualsNil(t *testing.T) {
	var m1, m2 *money.Money
	if !m1.Equals(m2) {
		t.Error("nil equality check failed")
	}
}

func TestMoneyEqualsSelf(t *testing.T) {
	m1 := money.EUR(123)
	if !m1.Equals(m1) {
		t.Error("self equality check failed")
	}
}

func TestMoneyNotEqualsNil(t *testing.T) {
	m1 := money.EUR(123)
	if m1.Equals(nil) {
		t.Error("nil equality check did not fail")
	}
}

func TestMoneyNilNotEquals(t *testing.T) {
	var m1 *money.Money
	if m1.Equals(money.EUR(1)) {
		t.Error("nil equality check did not fail")
	}
}

func TestMoneyDebitZero(t *testing.T) {
	m1 := money.HKD(0).Debit()

	a := m1.String()
	e := "HKD 0.00"

	if a != e {
		t.Errorf("got zero debit %q. expected %q", a, e)
	}
}

func TestMoneyDebitZeroEqualsCreditZero(t *testing.T) {
	c := money.CAD(0)
	d := money.CAD(0).Debit()

	if !c.Equals(d) {
		t.Errorf("credit/debit zero failed equality check")
	}
}

func TestDivideByZero(t *testing.T) {
	m1 := money.EUR(100)
	_, err := m1.Div(0)
	if err != money.ErrDivZero {
		t.Errorf("divide by zero returned with unexpected error %q. expected %q", err, money.ErrDivZero)
	}
}

func TestAddDiffCurr(t *testing.T) {
	m1 := money.EUR(100)
	m2 := money.USD(100)

	_, err := m1.Add(m2)
	if err != money.ErrDiffCurrencies {
		t.Errorf("adding money of different currencies with unexpected error %q. expected %q", err, money.ErrDiffCurrencies)
	}
}

func TestAddNil(t *testing.T) {
	var m1, m2 *money.Money
	m1 = money.EUR(100)
	if _, err := m1.Add(m2); err != money.ErrMissingParam {
		t.Errorf("add nil with unexpected error %q. expected %q", err, money.ErrMissingParam)
	}

	if _, err := m2.Add(m1); err != money.ErrMissingParam {
		t.Errorf("add to a nil with unexpected error %q. expected %q", err, money.ErrMissingParam)
	}
}

func TestSubNil(t *testing.T) {
	var m1, m2 *money.Money
	m1 = money.EUR(100)

	if _, err := m1.Sub(m2); err != money.ErrMissingParam {
		t.Errorf("subtract nil with unexpected error %q. expected %q", err, money.ErrMissingParam)
	}

	if _, err := m2.Sub(m1); err != money.ErrMissingParam {
		t.Errorf("sub from a nil with unexpected error %q. expected %q", err, money.ErrMissingParam)
	}
}

func TestPercentNil(t *testing.T) {
	var m1 *money.Money

	if _, err := m1.Percent(1); err != money.ErrMissingParam {
		t.Errorf("percentage of nil with unexpected error %q. expected %q", err, money.ErrMissingParam)
	}
}

func TestCentsOfNil(t *testing.T) {
	var m1 *money.Money
	a := m1.Cents()
	e := uint64(0)
	if a != e {
		t.Errorf("unexpected count of cents from a nil money %d. expected %q", a, e)
	}
}

func TestMoneyIsDebit(t *testing.T) {
	var m1 *money.Money

	if m1.IsDebit() {
		t.Error("nil money should not be debit")
	}

	m1 = money.EUR(1)
	if m1.IsDebit() {
		t.Error("credit should not be debit")
	}

	m1 = m1.Debit()
	if !m1.IsDebit() {
		t.Errorf("credit should be debit")
	}
}
<commit_msg>style: rename examples<commit_after>package money_test

import (
	"fmt"
	"testing"

	"github.com/fgrid/money"
)

func ExampleNew() {
	m := money.New(123, money.NewCurrency("USD"))
	fmt.Println(m)
	// Output: USD 1.23
}

func ExampleMoney_Cents() {
	fmt.Println(money.AUD(1).Cents())
	// Output: 1
}

func ExampleMoney_String() {
	fmt.Println(money.EUR(1))
	// Output: EUR 0.01
}

func ExampleMoney_String_second() {
	fmt.Println(money.EUR(100))
	// Output: EUR 1.00
}

func ExampleMoney_String_third() {
	fmt.Println(money.EUR(100).Debit())
	// Output: EUR -1.00
}

func ExampleMoney_Add() {
	m1 := money.EUR(1)
	m2 := money.EUR(100)
	m3, _ := m1.Add(m2)
	fmt.Println(m3)
	// Output: EUR 1.01
}

func ExampleMoney_Add_second() {
	m1 := money.EUR(1).Debit()
	m2, _ := m1.Add(m1)
	fmt.Println(m2)
	// Output: EUR -0.02
}

func ExampleMoney_Add_third() {
	m1 := money.EUR(1)
	m2 := money.USD(1)
	_, err := m1.Add(m2)
	fmt.Println(err.Error())
	// Output: different currencies not allowed
}

func ExampleMoney_Sub() {
	m1 := money.GBP(1)
	m2 := money.GBP(100)

	m3, _ := m1.Sub(m2)
	m4, _ := m2.Sub(m1)

	fmt.Println(m3)
	fmt.Println(m4)
	// Output:
	// GBP -0.99
	// GBP 0.99
}

func ExampleMoney_Inv() {
	m1 := money.CHF(1)
	m2 := m1.Inv()
	m3 := m2.Inv()

	fmt.Println(m1)
	fmt.Println(m2)
	fmt.Println(m3)

	// Output:
	// CHF 0.01
	// CHF -0.01
	// CHF 0.01
}

func ExampleMoney_Mul() {
	m1 := money.JPY(51)
	m2, _ := m1.Mul(3)

	fmt.Println(m2)
	// Output: JPY 1.53
}

func ExampleMoney_Percent() {
	m1 := money.CNY(100)
	m2, _ := m1.Percent(33)

	fmt.Println(m2)
	// Output: CNY 0.33
}

func ExampleMoney_Div_roundingUp() {
	u1, _ := money.EUR(105).Div(10)
	u2, _ := money.EUR(106).Div(10)

	fmt.Println(u1)
	fmt.Println(u2)
	// Output:
	// EUR 0.11
	// EUR 0.11
}

func ExampleMoney_Div_roundingDown() {
	d1, _ := money.EUR(104).Div(10)
	d2, _ := money.EUR(100).Div(10)

	fmt.Println(d1)
	fmt.Println(d2)
	// Output:
	// EUR 0.10
	// EUR 0.10
}

func ExampleMoney_Div_roundingHalfAwayFromZero() {
	d1, _ := money.EUR(105).Debit().Div(10)
	u2, _ := money.EUR(105).Div(10)

	fmt.Println(d1)
	fmt.Println(u2)
	// Output:
	// EUR -0.11
	// EUR 0.11
}

func TestMoneyEquals(t *testing.T) {
	m1 := money.INR(101)
	m2, _ := m1.Add(money.INR(1))
	m3, _ := m2.Sub(money.INR(1))

	if !m1.Equals(m3) {
		t.Error("failed equality check")
	}
}

func TestMoneyEqualsDebits(t *testing.T) {
	m1 := money.RUB(101).Debit()
	m2 := money.RUB(101).Credit()

	if m1.Equals(m2) {
		t.Error("failed equality check by debits")
	}
}

func TestMoneyNilEqualsNil(t *testing.T) {
	var m1, m2 *money.Money
	if !m1.Equals(m2) {
		t.Error("nil equality check failed")
	}
}

func TestMoneyEqualsSelf(t *testing.T) {
	m1 := money.EUR(123)
	if !m1.Equals(m1) {
		t.Error("self equality check failed")
	}
}

func TestMoneyNotEqualsNil(t *testing.T) {
	m1 := money.EUR(123)
	if m1.Equals(nil) {
		t.Error("nil equality check did not fail")
	}
}

func TestMoneyNilNotEquals(t *testing.T) {
	var m1 *money.Money
	if m1.Equals(money.EUR(1)) {
		t.Error("nil equality check did not fail")
	}
}

func TestMoneyDebitZero(t *testing.T) {
	m1 := money.HKD(0).Debit()

	a := m1.String()
	e := "HKD 0.00"

	if a != e {
		t.Errorf("got zero debit %q. expected %q", a, e)
	}
}

func TestMoneyDebitZeroEqualsCreditZero(t *testing.T) {
	c := money.CAD(0)
	d := money.CAD(0).Debit()

	if !c.Equals(d) {
		t.Errorf("credit/debit zero failed equality check")
	}
}

func TestDivideByZero(t *testing.T) {
	m1 := money.EUR(100)
	_, err := m1.Div(0)
	if err != money.ErrDivZero {
		t.Errorf("divide by zero returned with unexpected error %q. expected %q", err, money.ErrDivZero)
	}
}

func TestAddDiffCurr(t *testing.T) {
	m1 := money.EUR(100)
	m2 := money.USD(100)

	_, err := m1.Add(m2)
	if err != money.ErrDiffCurrencies {
		t.Errorf("adding money of different currencies with unexpected error %q. expected %q", err, money.ErrDiffCurrencies)
	}
}

func TestAddNil(t *testing.T) {
	var m1, m2 *money.Money
	m1 = money.EUR(100)
	if _, err := m1.Add(m2); err != money.ErrMissingParam {
		t.Errorf("add nil with unexpected error %q. expected %q", err, money.ErrMissingParam)
	}

	if _, err := m2.Add(m1); err != money.ErrMissingParam {
		t.Errorf("add to a nil with unexpected error %q. expected %q", err, money.ErrMissingParam)
	}
}

func TestSubNil(t *testing.T) {
	var m1, m2 *money.Money
	m1 = money.EUR(100)

	if _, err := m1.Sub(m2); err != money.ErrMissingParam {
		t.Errorf("subtract nil with unexpected error %q. expected %q", err, money.ErrMissingParam)
	}

	if _, err := m2.Sub(m1); err != money.ErrMissingParam {
		t.Errorf("sub from a nil with unexpected error %q. expected %q", err, money.ErrMissingParam)
	}
}

func TestPercentNil(t *testing.T) {
	var m1 *money.Money

	if _, err := m1.Percent(1); err != money.ErrMissingParam {
		t.Errorf("percentage of nil with unexpected error %q. expected %q", err, money.ErrMissingParam)
	}
}

func TestCentsOfNil(t *testing.T) {
	var m1 *money.Money
	a := m1.Cents()
	e := uint64(0)
	if a != e {
		t.Errorf("unexpected count of cents from a nil money %d. expected %q", a, e)
	}
}

func TestMoneyIsDebit(t *testing.T) {
	var m1 *money.Money

	if m1.IsDebit() {
		t.Error("nil money should not be debit")
	}

	m1 = money.EUR(1)
	if m1.IsDebit() {
		t.Error("credit should not be debit")
	}

	m1 = m1.Debit()
	if !m1.IsDebit() {
		t.Errorf("credit should be debit")
	}
}
<|endoftext|>


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

package mongostore

import (
	"errors"
	"github.com/gorilla/securecookie"
	"github.com/gorilla/sessions"
	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
	"net/http"
	"time"
)

var (
	ErrInvalidId = errors.New("mgostore: invalid session id")
)

// Session object store in MongoDB
type Session struct {
	Id       bson.ObjectId `bson:"_id,omitempty"`
	Data     string
	Modified time.Time
}

// MongoStore stores sessions in MongoDB
type MongoStore struct {
	Codecs  []securecookie.Codec
	Options *sessions.Options
	Token   TokenGetSeter
	coll    *mgo.Collection
}

// NewMongoStore returns a new MongoStore.
// Set ensureTTL to true let the database auto-remove expired object by maxAge.
func NewMongoStore(c *mgo.Collection, maxAge int, ensureTTL bool,
	keyPairs ...[]byte) *MongoStore {
	store := &MongoStore{
		Codecs: securecookie.CodecsFromPairs(keyPairs...),
		Options: &sessions.Options{
			Path: "/",
			MaxAge: maxAge,
		},
		Token: &CookieToken{},
		coll:  c,
	}

	if ensureTTL {
		c.EnsureIndex(mgo.Index{
			Key:         []string{"modified"},
			Background:  true,
			Sparse:      true,
			ExpireAfter: time.Duration(maxAge) * time.Second,
		})
	}

	return store
}

// Get registers and returns a session for the given name and session store.
// It returns a new session if there are no sessions registered for the name.
func (m *MongoStore) Get(r *http.Request, name string) (
	*sessions.Session, error) {
	return sessions.GetRegistry(r).Get(m, name)
}

// New returns a session for the given name without adding it to the registry.
func (m *MongoStore) New(r *http.Request, name string) (
	*sessions.Session, error) {
	session := sessions.NewSession(m, name)
	session.Options = &sessions.Options{
		Path:   m.Options.Path,
		MaxAge: m.Options.MaxAge,
	}
	session.IsNew = true
	var err error
	if cook, errToken := m.Token.GetToken(r, name); errToken == nil {
		err = securecookie.DecodeMulti(name, cook, &session.ID, m.Codecs...)
		if err == nil {
			err = m.load(session)
			if err == nil {
				session.IsNew = false
			} else {
				err = nil
			}
		}
	}
	return session, err
}

// Save saves all sessions registered for the current request.
func (m *MongoStore) Save(r *http.Request, w http.ResponseWriter,
	session *sessions.Session) error {
	if session.Options.MaxAge < 0 {
		if err := m.delete(session); err != nil {
			return err
		}
		m.Token.SetToken(w, session.Name(), "", session.Options)
		return nil
	}

	if session.ID == "" {
		session.ID = bson.NewObjectId().Hex()
	}

	if err := m.upsert(session); err != nil {
		return err
	}

	encoded, err := securecookie.EncodeMulti(session.Name(), session.ID,
		m.Codecs...)
	if err != nil {
		return err
	}

	m.Token.SetToken(w, session.Name(), encoded, session.Options)
	return nil
}

func (m *MongoStore) load(session *sessions.Session) error {
	if !bson.IsObjectIdHex(session.ID) {
		return ErrInvalidId
	}

	s := Session{}
	err := m.coll.FindId(bson.ObjectIdHex(session.ID)).One(&s)
	if err != nil {
		return err
	}

	if err := securecookie.DecodeMulti(session.Name(), s.Data, &session.Values,
		m.Codecs...); err != nil {
		return err
	}

	return nil
}

func (m *MongoStore) upsert(session *sessions.Session) error {
	if !bson.IsObjectIdHex(session.ID) {
		return ErrInvalidId
	}

	var modified time.Time
	if val, ok := session.Values["modified"]; ok {
		modified, ok = val.(time.Time)
		if !ok {
			return errors.New("mongostore: invalid modified value")
		}
	} else {
		modified = time.Now()
	}

	encoded, err := securecookie.EncodeMulti(session.Name(), session.Values,
		m.Codecs...)
	if err != nil {
		return err
	}

	s := Session{
		Id:       bson.ObjectIdHex(session.ID),
		Data:     encoded,
		Modified: modified,
	}

	_, err = m.coll.UpsertId(s.Id, &s)
	if err != nil {
		return err
	}

	return nil
}

func (m *MongoStore) delete(session *sessions.Session) error {
	if !bson.IsObjectIdHex(session.ID) {
		return ErrInvalidId
	}

	return m.coll.RemoveId(bson.ObjectIdHex(session.ID))
}
<commit_msg>Get all options available when create new session<commit_after>// Copyright 2012 The KidStuff Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package mongostore

import (
	"errors"
	"github.com/gorilla/securecookie"
	"github.com/gorilla/sessions"
	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
	"net/http"
	"time"
)

var (
	ErrInvalidId = errors.New("mgostore: invalid session id")
)

// Session object store in MongoDB
type Session struct {
	Id       bson.ObjectId `bson:"_id,omitempty"`
	Data     string
	Modified time.Time
}

// MongoStore stores sessions in MongoDB
type MongoStore struct {
	Codecs  []securecookie.Codec
	Options *sessions.Options
	Token   TokenGetSeter
	coll    *mgo.Collection
}

// NewMongoStore returns a new MongoStore.
// Set ensureTTL to true let the database auto-remove expired object by maxAge.
func NewMongoStore(c *mgo.Collection, maxAge int, ensureTTL bool,
	keyPairs ...[]byte) *MongoStore {
	store := &MongoStore{
		Codecs: securecookie.CodecsFromPairs(keyPairs...),
		Options: &sessions.Options{
			Path: "/",
			MaxAge: maxAge,
		},
		Token: &CookieToken{},
		coll:  c,
	}

	if ensureTTL {
		c.EnsureIndex(mgo.Index{
			Key:         []string{"modified"},
			Background:  true,
			Sparse:      true,
			ExpireAfter: time.Duration(maxAge) * time.Second,
		})
	}

	return store
}

// Get registers and returns a session for the given name and session store.
// It returns a new session if there are no sessions registered for the name.
func (m *MongoStore) Get(r *http.Request, name string) (
	*sessions.Session, error) {
	return sessions.GetRegistry(r).Get(m, name)
}

// New returns a session for the given name without adding it to the registry.
func (m *MongoStore) New(r *http.Request, name string) (
	*sessions.Session, error) {
	session := sessions.NewSession(m, name)
	session.Options = &sessions.Options{
		Path:     m.Options.Path,
		MaxAge:   m.Options.MaxAge,
		Domain:   m.Options.Domain,
		Secure:   m.Options.Secure,
		HttpOnly: m.Options.HttpOnly,
	}
	session.IsNew = true
	var err error
	if cook, errToken := m.Token.GetToken(r, name); errToken == nil {
		err = securecookie.DecodeMulti(name, cook, &session.ID, m.Codecs...)
		if err == nil {
			err = m.load(session)
			if err == nil {
				session.IsNew = false
			} else {
				err = nil
			}
		}
	}
	return session, err
}

// Save saves all sessions registered for the current request.
func (m *MongoStore) Save(r *http.Request, w http.ResponseWriter,
	session *sessions.Session) error {
	if session.Options.MaxAge < 0 {
		if err := m.delete(session); err != nil {
			return err
		}
		m.Token.SetToken(w, session.Name(), "", session.Options)
		return nil
	}

	if session.ID == "" {
		session.ID = bson.NewObjectId().Hex()
	}

	if err := m.upsert(session); err != nil {
		return err
	}

	encoded, err := securecookie.EncodeMulti(session.Name(), session.ID,
		m.Codecs...)
	if err != nil {
		return err
	}

	m.Token.SetToken(w, session.Name(), encoded, session.Options)
	return nil
}

func (m *MongoStore) load(session *sessions.Session) error {
	if !bson.IsObjectIdHex(session.ID) {
		return ErrInvalidId
	}

	s := Session{}
	err := m.coll.FindId(bson.ObjectIdHex(session.ID)).One(&s)
	if err != nil {
		return err
	}

	if err := securecookie.DecodeMulti(session.Name(), s.Data, &session.Values,
		m.Codecs...); err != nil {
		return err
	}

	return nil
}

func (m *MongoStore) upsert(session *sessions.Session) error {
	if !bson.IsObjectIdHex(session.ID) {
		return ErrInvalidId
	}

	var modified time.Time
	if val, ok := session.Values["modified"]; ok {
		modified, ok = val.(time.Time)
		if !ok {
			return errors.New("mongostore: invalid modified value")
		}
	} else {
		modified = time.Now()
	}

	encoded, err := securecookie.EncodeMulti(session.Name(), session.Values,
		m.Codecs...)
	if err != nil {
		return err
	}

	s := Session{
		Id:       bson.ObjectIdHex(session.ID),
		Data:     encoded,
		Modified: modified,
	}

	_, err = m.coll.UpsertId(s.Id, &s)
	if err != nil {
		return err
	}

	return nil
}

func (m *MongoStore) delete(session *sessions.Session) error {
	if !bson.IsObjectIdHex(session.ID) {
		return ErrInvalidId
	}

	return m.coll.RemoveId(bson.ObjectIdHex(session.ID))
}
<|endoftext|>