<commit_before>package main

import (
	"html/template"
	"net/http"
	"os"
	"regexp"

	"log"

	"strings"

	"time"

	"strconv"

	"github.com/microcosm-cc/bluemonday"
	"github.com/russross/blackfriday"

	rake "github.com/afjoseph/RAKE.Go"
)

var wikiDir string
var tagDir string
var pubDir string
var ekey []byte

var encryptionFlag = []byte("ENCRYPTED")

var specialDir []string

type basePage struct {
	Title string
	Nav   nav
}
type wikiPage struct {
	Body      template.HTML
	Tags      string
	TagArray  []string
	Created   string
	Modified  string
	Published bool
	Encrypted bool
	basePage
	Index []string
}
type searchPage struct {
	basePage
	Results []QueryResults
}

func checkErr(err error) {
	if err != nil {
		panic(err)
	}
}
func getPDFFilename(folder, name string) string {
	return folder + name
}

func getWikiFilename(folder, name string) string {
	return folder + name + ".md"
}

func getWikiTagsFilename(name string) string {
	return tagDir + name
}
func getWikiPubFilename(name string) string {
	return pubDir + name
}
func (p *wikiPage) save(s storage) error {
	var err error
	filename := getWikiFilename(wikiDir, p.Title)
	body := []byte(p.Body)
	if p.Encrypted {
		body, err = encrypt(body, ekey)
		if err != nil {
			return err
		}
		body = append(encryptionFlag, body...)
	}
	err = s.storeFile(filename, body)
	if err != nil {
		return err
	}

	tagsfile := getWikiTagsFilename(p.Title)
	err = s.storeFile(tagsfile, []byte(p.Tags))
	if err != nil {
		return err
	}

	log.Printf("Pub flag %v\n", p.Published)
	pubfile := getWikiPubFilename(p.Title)
	if p.Published {
		log.Printf("Saving %v\n", pubfile)
		err = s.storeFile(pubfile, nil)
		if err != nil {
			return err
		}

	} else {
		log.Printf("Removing %v\n", pubfile)
		// Only return an error if something other than file doesnt exit
		// We expect this fail to not exist most of the time but we dont know if
		// we don't try
		if err = s.deleteFile(pubfile); !os.IsNotExist(err) {
			return err
		}
	}

	return nil
}

func convertMarkdown(page *wikiPage, err error) (*wikiPage, error) {
	if err != nil {
		return page, err
	}
	index := rake.RunRake(string(page.Body))
	for _, v := range index {
		if v.Value < 4 {
			break
		}
		page.Index = append(page.Index, v.Key)
	}

	p := bluemonday.UGCPolicy()
	p.AllowAttrs("class").Matching(regexp.MustCompile("^language-[a-zA-Z0-9]+$")).OnElements("code")

	page.Body = template.HTML(regexp.MustCompile("\r\n").ReplaceAllString(string(page.Body), "\n"))

	unsafe := blackfriday.Run([]byte(page.Body),
		blackfriday.WithExtensions(
			blackfriday.CommonExtensions|
				blackfriday.HardLineBreak|
				blackfriday.HeadingIDs|
				blackfriday.AutoHeadingIDs,
		),
	)

	page.Body = template.HTML(p.SanitizeBytes(unsafe))
	return page, nil

}
func viewHandler(w http.ResponseWriter, r *http.Request, p *wikiPage, s storage) {
	p, err := convertMarkdown(s.getPage(p))
	if err != nil {
		p, err = s.checkForPDF(p)
		if err != nil {
			http.Redirect(w, r, "/wiki/edit/"+p.Title, http.StatusFound)
			return
		}
	} else {
		p.Body = template.HTML(parseWikiWords([]byte(p.Body)))
	}

	renderTemplate(w, "view", p)
}
func todoHandler(w http.ResponseWriter, r *http.Request, p *wikiPage, s storage) {
	p, err := convertMarkdown(s.getPage(p))
	if err != nil {
		p, err = s.checkForPDF(p)
		if err != nil {
			http.Redirect(w, r, "/wiki/edit/"+p.Title, http.StatusFound)
			return
		}
	} else {
		p.Body = template.HTML(parseWikiWords([]byte(p.Body)))
	}

	renderTemplate(w, "view", p)
}

func editHandler(w http.ResponseWriter, r *http.Request, p *wikiPage, s storage) {
	p, _ = s.getPage(p)
	renderTemplate(w, "edit", p)
}

func makeSearchHandler(fn navFunc, s storage) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		term := r.URL.Query().Get("term") // Get the search term
		if len(term) == 0 {
			http.NotFound(w, r)
			return
		}

		results := ParseQueryResults(s.searchPages(wikiDir, term))
		p := &searchPage{Results: results, basePage: basePage{Title: "Search", Nav: fn(s)}}

		renderTemplate(w, "search", p)
	}
}

func simpleHandler(page string, fn navFunc, s storage) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		renderTemplate(w, page, fn(s))
	}

}

func saveHandler(w http.ResponseWriter, r *http.Request, wiki string, s storage) string {
	body := r.FormValue("body")

	body = regexp.MustCompile("\r\n").ReplaceAllString(body, "\n")

	log.Printf("Checkbox is : %v", r.FormValue("wikipub"))
	p := wikiPage{basePage: basePage{Title: wiki}, Body: template.HTML(body), Tags: r.FormValue("wikitags")}
	if r.FormValue("wikipub") == "on" {
		p.Published = true
	}
	if r.FormValue("wikicrypt") == "on" {
		p.Encrypted = true
	}

	err := p.save(s)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return ""
	}
	http.Redirect(w, r, "/wiki/view/"+p.Title, http.StatusFound)

	return r.FormValue("wikitags")
}

func deleteHandler(w http.ResponseWriter, r *http.Request, p *wikiPage, s storage) {

	filename := getWikiFilename(wikiDir, p.Title)
	log.Printf("Gonna delete : %v", filename)

	if err := s.deleteFile(filename); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	tagsfile := getWikiTagsFilename(p.Title)
	log.Printf("and tags to delete : %v", tagsfile)
	if err := s.deleteFile(tagsfile); err != nil && !os.IsNotExist(err) {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	http.Redirect(w, r, "/wiki", http.StatusFound)
}
func moveHandler(w http.ResponseWriter, r *http.Request, p *wikiPage, s storage) {

	from := getWikiFilename(wikiDir, p.Title)
	to := r.FormValue("to")
	if len(to) == 0 {
		http.Error(w, "Form param 'to' needs setting", http.StatusBadRequest)
	}
	tofile := getWikiFilename(wikiDir, to)

	log.Printf("Gonna move : %v to %v", from, tofile)

	if err := s.moveFile(from, tofile); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	tagsfile := getWikiTagsFilename(p.Title)
	totags := getWikiTagsFilename(to)
	log.Printf("and tags to move : %v to %v", tagsfile, totags)
	if err := s.moveFile(tagsfile, totags); err != nil && !os.IsNotExist(err) {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	http.Redirect(w, r, "/wiki/view/"+to, http.StatusFound)
}

var templates = template.Must(template.ParseFiles(
	"views/edit.html",
	"views/view.html",
	"views/pub.html",
	"views/pubhome.html",
	"views/home.html",
	"views/todo.html",
	"views/search.html",
	"views/index.html",
	"views/footer.html",
	"views/leftnav.html"))

func renderTemplate(w http.ResponseWriter, tmpl string, p interface{}) {
	err := templates.ExecuteTemplate(w, tmpl+".html", p)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

var validPath = regexp.MustCompile(`^/wiki/(edit|save|view|search|delete|move|todo)/([a-zA-Z0-9\.\-_ /]*)$`)

func makeHandler(fn func(http.ResponseWriter, *http.Request, *wikiPage, storage), navfn navFunc, s storage) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		wword := r.URL.Query().Get("wword") // Get the wiki word param if available
		if len(wword) == 0 {
			log.Printf("Path is : %v", r.URL.Path)
			m := validPath.FindStringSubmatch(r.URL.Path)
			if m == nil {
				http.NotFound(w, r)
				return
			}
			wword = m[2]
		}
		p := &wikiPage{basePage: basePage{Title: wword, Nav: navfn(s)}}
		fn(w, r, p, s)
	}
}

func processSave(fn func(http.ResponseWriter, *http.Request, string, storage) string, s storage) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		m := validPath.FindStringSubmatch(r.URL.Path)
		if m == nil {
			http.NotFound(w, r)
			return
		}
		fn(w, r, m[2], s)

	}
}

func parseWikiWords(target []byte) []byte {
	var wikiWord = regexp.MustCompile(`\{\{([^\}^#]+)[#]*(.*)\}\}`)

	return wikiWord.ReplaceAll(target, []byte("$1"))
}

func loggingHandler(next http.Handler) http.Handler {
	fn := func(w http.ResponseWriter, r *http.Request) {
		t1 := time.Now()
		next.ServeHTTP(w, r)
		t2 := time.Now()
		log.Printf("[%s] %q %v\n", r.Method, r.URL.String(), t2.Sub(t1))
	}
	return http.HandlerFunc(fn)
}

func main() {
	specialDir = []string{"tags", "pub"}
	config, err := LoadConfig()
	if err != nil {
		log.Fatal(err)
	}

	if config.Logfile != "" {
		f, err := os.OpenFile(config.Logfile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
		if err != nil {
			checkErr(err)
		}
		defer f.Close()

		log.SetOutput(f)
	}

	wikiDir = config.WikiDir
	if !strings.HasSuffix(wikiDir, "/") {
		wikiDir = wikiDir + "/"
	}
	tagDir = wikiDir + "tags/" // Make sure this doesnt double up the / in the path...
	pubDir = wikiDir + "pub/"  // Make sure this doesnt double up the / in the path...

	ekey = []byte(config.EncryptionKey)

	os.Mkdir(config.WikiDir, 0755)
	os.Mkdir(config.WikiDir+"tags", 0755)

	httpmux := http.NewServeMux()

	cached := newCachedStorage(fileStorage{tagDir}, wikiDir, tagDir)
	fstore := &cached

	httpmux.Handle("/wiki", loggingHandler(simpleHandler("home", getNav, fstore)))
	httpmux.Handle("/wiki/search/", loggingHandler(makeSearchHandler(getNav, fstore)))
	httpmux.Handle("/wiki/view/", loggingHandler(makeHandler(viewHandler, getNav, fstore)))
	httpmux.Handle("/wiki/edit/", loggingHandler(makeHandler(editHandler, getNav, fstore)))
	httpmux.Handle("/wiki/todo/", loggingHandler(simpleHandler("todo", getNav, fstore)))
	httpmux.Handle("/wiki/save/", loggingHandler(processSave(saveHandler, fstore)))
	httpmux.Handle("/wiki/delete/", loggingHandler(makeHandler(deleteHandler, getNav, fstore)))
	httpmux.Handle("/wiki/move/", loggingHandler(makeHandler(moveHandler, getNav, fstore)))
	httpmux.Handle("/wiki/raw/", http.StripPrefix("/wiki/raw/", http.FileServer(http.Dir(wikiDir))))
	httpmux.Handle("/pub/", loggingHandler(makePubHandler(pubHandler, getNav, fstore)))
	httpmux.Handle("/pub", loggingHandler(simpleHandler("pubhome", getPubNav, fstore)))
	httpmux.Handle("/api", loggingHandler(apiHandler(innerAPIHandler, fstore)))

	// Listen for normal traffic against root
	httpmux.Handle("/", http.FileServer(http.Dir("wwwroot")))
	httpmux.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))
	err = http.ListenAndServe(":"+strconv.Itoa(config.HTTPPort), httpmux)
	checkErr(err)

}
<commit_msg>Remove rake - the summariser of wiki pages<commit_after>package main

import (
	"html/template"
	"net/http"
	"os"
	"regexp"

	"log"

	"strings"

	"time"

	"strconv"

	"github.com/microcosm-cc/bluemonday"
	"github.com/russross/blackfriday"
)

var wikiDir string
var tagDir string
var pubDir string
var ekey []byte

var encryptionFlag = []byte("ENCRYPTED")

var specialDir []string

type basePage struct {
	Title string
	Nav   nav
}
type wikiPage struct {
	Body      template.HTML
	Tags      string
	TagArray  []string
	Created   string
	Modified  string
	Published bool
	Encrypted bool
	basePage
	Index []string
}
type searchPage struct {
	basePage
	Results []QueryResults
}

func checkErr(err error) {
	if err != nil {
		panic(err)
	}
}
func getPDFFilename(folder, name string) string {
	return folder + name
}

func getWikiFilename(folder, name string) string {
	return folder + name + ".md"
}

func getWikiTagsFilename(name string) string {
	return tagDir + name
}
func getWikiPubFilename(name string) string {
	return pubDir + name
}
func (p *wikiPage) save(s storage) error {
	var err error
	filename := getWikiFilename(wikiDir, p.Title)
	body := []byte(p.Body)
	if p.Encrypted {
		body, err = encrypt(body, ekey)
		if err != nil {
			return err
		}
		body = append(encryptionFlag, body...)
	}
	err = s.storeFile(filename, body)
	if err != nil {
		return err
	}

	tagsfile := getWikiTagsFilename(p.Title)
	err = s.storeFile(tagsfile, []byte(p.Tags))
	if err != nil {
		return err
	}

	log.Printf("Pub flag %v\n", p.Published)
	pubfile := getWikiPubFilename(p.Title)
	if p.Published {
		log.Printf("Saving %v\n", pubfile)
		err = s.storeFile(pubfile, nil)
		if err != nil {
			return err
		}

	} else {
		log.Printf("Removing %v\n", pubfile)
		// Only return an error if something other than file doesnt exit
		// We expect this fail to not exist most of the time but we dont know if
		// we don't try
		if err = s.deleteFile(pubfile); !os.IsNotExist(err) {
			return err
		}
	}

	return nil
}

func convertMarkdown(page *wikiPage, err error) (*wikiPage, error) {
	if err != nil {
		return page, err
	}

	// Used to run rake which was a summarizer for the page...however, it was a bit
	// rubbish so have abandoned it.
	// index := rake.RunRake(string(page.Body))
	// for _, v := range index {
	// 	if v.Value < 4 {
	// 		break
	// 	}
	// 	page.Index = append(page.Index, v.Key)
	// }

	p := bluemonday.UGCPolicy()
	p.AllowAttrs("class").Matching(regexp.MustCompile("^language-[a-zA-Z0-9]+$")).OnElements("code")

	page.Body = template.HTML(regexp.MustCompile("\r\n").ReplaceAllString(string(page.Body), "\n"))

	unsafe := blackfriday.Run([]byte(page.Body),
		blackfriday.WithExtensions(
			blackfriday.CommonExtensions|
				blackfriday.HardLineBreak|
				blackfriday.HeadingIDs|
				blackfriday.AutoHeadingIDs,
		),
	)

	page.Body = template.HTML(p.SanitizeBytes(unsafe))
	return page, nil

}
func viewHandler(w http.ResponseWriter, r *http.Request, p *wikiPage, s storage) {
	p, err := convertMarkdown(s.getPage(p))
	if err != nil {
		p, err = s.checkForPDF(p)
		if err != nil {
			http.Redirect(w, r, "/wiki/edit/"+p.Title, http.StatusFound)
			return
		}
	} else {
		p.Body = template.HTML(parseWikiWords([]byte(p.Body)))
	}

	renderTemplate(w, "view", p)
}
func todoHandler(w http.ResponseWriter, r *http.Request, p *wikiPage, s storage) {
	p, err := convertMarkdown(s.getPage(p))
	if err != nil {
		p, err = s.checkForPDF(p)
		if err != nil {
			http.Redirect(w, r, "/wiki/edit/"+p.Title, http.StatusFound)
			return
		}
	} else {
		p.Body = template.HTML(parseWikiWords([]byte(p.Body)))
	}

	renderTemplate(w, "view", p)
}

func editHandler(w http.ResponseWriter, r *http.Request, p *wikiPage, s storage) {
	p, _ = s.getPage(p)
	renderTemplate(w, "edit", p)
}

func makeSearchHandler(fn navFunc, s storage) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		term := r.URL.Query().Get("term") // Get the search term
		if len(term) == 0 {
			http.NotFound(w, r)
			return
		}

		results := ParseQueryResults(s.searchPages(wikiDir, term))
		p := &searchPage{Results: results, basePage: basePage{Title: "Search", Nav: fn(s)}}

		renderTemplate(w, "search", p)
	}
}

func simpleHandler(page string, fn navFunc, s storage) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		renderTemplate(w, page, fn(s))
	}

}

func saveHandler(w http.ResponseWriter, r *http.Request, wiki string, s storage) string {
	body := r.FormValue("body")

	body = regexp.MustCompile("\r\n").ReplaceAllString(body, "\n")

	log.Printf("Checkbox is : %v", r.FormValue("wikipub"))
	p := wikiPage{basePage: basePage{Title: wiki}, Body: template.HTML(body), Tags: r.FormValue("wikitags")}
	if r.FormValue("wikipub") == "on" {
		p.Published = true
	}
	if r.FormValue("wikicrypt") == "on" {
		p.Encrypted = true
	}

	err := p.save(s)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return ""
	}
	http.Redirect(w, r, "/wiki/view/"+p.Title, http.StatusFound)

	return r.FormValue("wikitags")
}

func deleteHandler(w http.ResponseWriter, r *http.Request, p *wikiPage, s storage) {

	filename := getWikiFilename(wikiDir, p.Title)
	log.Printf("Gonna delete : %v", filename)

	if err := s.deleteFile(filename); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	tagsfile := getWikiTagsFilename(p.Title)
	log.Printf("and tags to delete : %v", tagsfile)
	if err := s.deleteFile(tagsfile); err != nil && !os.IsNotExist(err) {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	http.Redirect(w, r, "/wiki", http.StatusFound)
}
func moveHandler(w http.ResponseWriter, r *http.Request, p *wikiPage, s storage) {

	from := getWikiFilename(wikiDir, p.Title)
	to := r.FormValue("to")
	if len(to) == 0 {
		http.Error(w, "Form param 'to' needs setting", http.StatusBadRequest)
	}
	tofile := getWikiFilename(wikiDir, to)

	log.Printf("Gonna move : %v to %v", from, tofile)

	if err := s.moveFile(from, tofile); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	tagsfile := getWikiTagsFilename(p.Title)
	totags := getWikiTagsFilename(to)
	log.Printf("and tags to move : %v to %v", tagsfile, totags)
	if err := s.moveFile(tagsfile, totags); err != nil && !os.IsNotExist(err) {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	http.Redirect(w, r, "/wiki/view/"+to, http.StatusFound)
}

var templates = template.Must(template.ParseFiles(
	"views/edit.html",
	"views/view.html",
	"views/pub.html",
	"views/pubhome.html",
	"views/home.html",
	"views/todo.html",
	"views/search.html",
	"views/index.html",
	"views/footer.html",
	"views/leftnav.html"))

func renderTemplate(w http.ResponseWriter, tmpl string, p interface{}) {
	err := templates.ExecuteTemplate(w, tmpl+".html", p)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

var validPath = regexp.MustCompile(`^/wiki/(edit|save|view|search|delete|move|todo)/([a-zA-Z0-9\.\-_ /]*)$`)

func makeHandler(fn func(http.ResponseWriter, *http.Request, *wikiPage, storage), navfn navFunc, s storage) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		wword := r.URL.Query().Get("wword") // Get the wiki word param if available
		if len(wword) == 0 {
			log.Printf("Path is : %v", r.URL.Path)
			m := validPath.FindStringSubmatch(r.URL.Path)
			if m == nil {
				http.NotFound(w, r)
				return
			}
			wword = m[2]
		}
		p := &wikiPage{basePage: basePage{Title: wword, Nav: navfn(s)}}
		fn(w, r, p, s)
	}
}

func processSave(fn func(http.ResponseWriter, *http.Request, string, storage) string, s storage) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		m := validPath.FindStringSubmatch(r.URL.Path)
		if m == nil {
			http.NotFound(w, r)
			return
		}
		fn(w, r, m[2], s)

	}
}

func parseWikiWords(target []byte) []byte {
	var wikiWord = regexp.MustCompile(`\{\{([^\}^#]+)[#]*(.*)\}\}`)

	return wikiWord.ReplaceAll(target, []byte("$1"))
}

func loggingHandler(next http.Handler) http.Handler {
	fn := func(w http.ResponseWriter, r *http.Request) {
		t1 := time.Now()
		next.ServeHTTP(w, r)
		t2 := time.Now()
		log.Printf("[%s] %q %v\n", r.Method, r.URL.String(), t2.Sub(t1))
	}
	return http.HandlerFunc(fn)
}

func main() {
	specialDir = []string{"tags", "pub"}
	config, err := LoadConfig()
	if err != nil {
		log.Fatal(err)
	}

	if config.Logfile != "" {
		f, err := os.OpenFile(config.Logfile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
		if err != nil {
			checkErr(err)
		}
		defer f.Close()

		log.SetOutput(f)
	}

	wikiDir = config.WikiDir
	if !strings.HasSuffix(wikiDir, "/") {
		wikiDir = wikiDir + "/"
	}
	tagDir = wikiDir + "tags/" // Make sure this doesnt double up the / in the path...
	pubDir = wikiDir + "pub/"  // Make sure this doesnt double up the / in the path...

	ekey = []byte(config.EncryptionKey)

	os.Mkdir(config.WikiDir, 0755)
	os.Mkdir(config.WikiDir+"tags", 0755)

	httpmux := http.NewServeMux()

	cached := newCachedStorage(fileStorage{tagDir}, wikiDir, tagDir)
	fstore := &cached

	httpmux.Handle("/wiki", loggingHandler(simpleHandler("home", getNav, fstore)))
	httpmux.Handle("/wiki/search/", loggingHandler(makeSearchHandler(getNav, fstore)))
	httpmux.Handle("/wiki/view/", loggingHandler(makeHandler(viewHandler, getNav, fstore)))
	httpmux.Handle("/wiki/edit/", loggingHandler(makeHandler(editHandler, getNav, fstore)))
	httpmux.Handle("/wiki/todo/", loggingHandler(simpleHandler("todo", getNav, fstore)))
	httpmux.Handle("/wiki/save/", loggingHandler(processSave(saveHandler, fstore)))
	httpmux.Handle("/wiki/delete/", loggingHandler(makeHandler(deleteHandler, getNav, fstore)))
	httpmux.Handle("/wiki/move/", loggingHandler(makeHandler(moveHandler, getNav, fstore)))
	httpmux.Handle("/wiki/raw/", http.StripPrefix("/wiki/raw/", http.FileServer(http.Dir(wikiDir))))
	httpmux.Handle("/pub/", loggingHandler(makePubHandler(pubHandler, getNav, fstore)))
	httpmux.Handle("/pub", loggingHandler(simpleHandler("pubhome", getPubNav, fstore)))
	httpmux.Handle("/api", loggingHandler(apiHandler(innerAPIHandler, fstore)))

	// Listen for normal traffic against root
	httpmux.Handle("/", http.FileServer(http.Dir("wwwroot")))
	httpmux.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))
	err = http.ListenAndServe(":"+strconv.Itoa(config.HTTPPort), httpmux)
	checkErr(err)

}
<|endoftext|>


<commit_before>package lytics

import (
	"encoding/json"
	"strconv"
	"time"
)

const (
	workEndpoint     = "work/:id" // state
	workListEndpoint = "work"
)

type Work struct {
	Id               string          `json:"id"`
	Aid              int             `json:"aid"`
	AccountId        string          `json:"account_id"`
	UserId           string          `json:"user_id"`
	WorkStateVersion int             `json:"work_state_version"`
	Config           json.RawMessage `json:"config,omitempty"`
	WorkflowId       string          `json:"workflow_id"`
	Workflow         string          `json:"workflow"`
	Tag              string          `json:"tag"`
	Updated          time.Time       `json:"updated"`
	Created          time.Time       `json:"created"`
	AuthIds          []string        `json:"auth_ids,omitempty"`
	Hidden           bool            `json:"hidden"`
	RuntimeOverride  string          `json:"runtime"`
	VerboseLogging   bool            `json:"verbose_logging"`
}

// GetWork returns a single work
// https://www.getlytics.com/developers/rest-api#work-get
func (l *Client) GetWork(id string, state bool) (Work, error) {
	res := ApiResp{}
	data := Work{}

	// make the request
	err := l.Get(parseLyticsURL(workEndpoint, map[string]string{"id": id, "state": strconv.FormatBool(state)}), nil, nil, &res, &data)
	if err != nil {
		return data, err
	}

	return data, nil
}

// GetWorks returns all works
// https://www.getlytics.com/developers/rest-api#work
func (l *Client) GetWorks() ([]Work, error) {
	res := ApiResp{}
	data := []Work{}

	// make the request
	err := l.Get(workListEndpoint, nil, nil, &res, &data)
	if err != nil {
		return data, err
	}

	return data, nil
}

// Other Available Endpoints
// * POST    create work
// * DELETE  remove work
<commit_msg>Add statuscode to work struct.<commit_after>package lytics

import (
	"encoding/json"
	"strconv"
	"time"
)

const (
	workEndpoint     = "work/:id" // state
	workListEndpoint = "work"
)

type Work struct {
	Id               string          `json:"id"`
	Aid              int             `json:"aid"`
	AccountId        string          `json:"account_id"`
	UserId           string          `json:"user_id"`
	WorkStateVersion int             `json:"work_state_version"`
	Config           json.RawMessage `json:"config,omitempty"`
	WorkflowId       string          `json:"workflow_id"`
	Workflow         string          `json:"workflow"`
	Tag              string          `json:"tag"`
	Updated          time.Time       `json:"updated"`
	Created          time.Time       `json:"created"`
	AuthIds          []string        `json:"auth_ids,omitempty"`
	Hidden           bool            `json:"hidden"`
	RuntimeOverride  string          `json:"runtime"`
	VerboseLogging   bool            `json:"verbose_logging"`
	StatusCode       string          `json:"statuscode"`
}

// GetWork returns a single work
// https://www.getlytics.com/developers/rest-api#work-get
func (l *Client) GetWork(id string, state bool) (Work, error) {
	res := ApiResp{}
	data := Work{}

	// make the request
	err := l.Get(parseLyticsURL(workEndpoint, map[string]string{"id": id, "state": strconv.FormatBool(state)}), nil, nil, &res, &data)
	if err != nil {
		return data, err
	}

	return data, nil
}

// GetWorks returns all works
// https://www.getlytics.com/developers/rest-api#work
func (l *Client) GetWorks() ([]Work, error) {
	res := ApiResp{}
	data := []Work{}

	// make the request
	err := l.Get(workListEndpoint, nil, nil, &res, &data)
	if err != nil {
		return data, err
	}

	return data, nil
}

// Other Available Endpoints
// * POST    create work
// * DELETE  remove work
<|endoftext|>


<commit_before>// Package xlog is a logger coupled with HTTP net/context aware middleware.
//
// Features:
//
//   - Per request log context
//   - Per request and/or per message key/value fields
//   - Log levels (Debug, Info, Warn, Error)
//   - Custom output
//   - Automatically gathers request context like User-Agent, IP etc.
//   - Drops message rather than blocking execution
//
// It works best in combination with github.com/rs/xhandler.
package xlog // import "github.com/rs/xlog"

import (
	"fmt"
	"io"
	"os"
	"path"
	"runtime"
	"strings"
	"sync"
	"time"
)

// Logger is per request logger interface
type Logger interface {
	// Implements io.Writer so it can be set a output of log.Logger
	io.Writer

	// SetField sets a field on the logger's context. All future messages on this logger
	// will have this field set.
	SetField(name string, value interface{})
	// Debug logs a debug message. If last parameter is a map[string]string, it's content
	// is added as fields to the message.
	Debug(v ...interface{})
	// Debug logs a debug message with format. If last parameter is a map[string]string,
	// it's content is added as fields to the message.
	Debugf(format string, v ...interface{})
	// Info logs a info message. If last parameter is a map[string]string, it's content
	// is added as fields to the message.
	Info(v ...interface{})
	// Info logs a info message with format. If last parameter is a map[string]string,
	// it's content is added as fields to the message.
	Infof(format string, v ...interface{})
	// Warn logs a warning message. If last parameter is a map[string]string, it's content
	// is added as fields to the message.
	Warn(v ...interface{})
	// Warn logs a warning message with format. If last parameter is a map[string]string,
	// it's content is added as fields to the message.
	Warnf(format string, v ...interface{})
	// Error logs an error message. If last parameter is a map[string]string, it's content
	// is added as fields to the message.
	Error(v ...interface{})
	// Error logs an error message with format. If last parameter is a map[string]string,
	// it's content is added as fields to the message.
	Errorf(format string, v ...interface{})
	// Fatal logs an error message followed by a call to os.Exit(1). If last parameter is a
	// map[string]string, it's content is added as fields to the message.
	Fatal(v ...interface{})
	// Fatalf logs an error message with format followed by a call to ox.Exit(1). If last
	// parameter is a map[string]string, it's content is added as fields to the message.
	Fatalf(format string, v ...interface{})
}

// Config defines logger's config
type Config struct {
	// Level is the maximum level to output, logs with lower level are discarded
	Level Level
	// Fields defines default fields to use with all messages
	Fields map[string]interface{}
	// Output is the channel to use to write log messages to
	Output *OutputChannel
}

// F represents a set of log message fields string -> interface{}
type F map[string]interface{}

type logger struct {
	level  Level
	output *OutputChannel
	fields map[string]interface{}
}

// Common key names for log messages
const (
	KeyTime    = "time"
	KeyMessage = "message"
	KeyLevel   = "level"
	KeyFile    = "file"
)

var now = time.Now
var exit1 = func() { os.Exit(1) }

var loggerPool = sync.Pool{
	New: func() interface{} {
		return &logger{}
	},
}

// New manually creates a logger.
//
// This function should only be used out of a request. Use FromContext in request.
func New(c Config) Logger {
	l := loggerPool.Get().(*logger)
	l.level = c.Level
	l.output = c.Output
	for k, v := range c.Fields {
		l.SetField(k, v)
	}
	return l
}

func (l *logger) close() {
	l.level = 0
	l.output = nil
	l.fields = nil
	loggerPool.Put(l)
}

func (l *logger) send(level Level, calldepth int, msg string, fields map[string]interface{}) {
	if level < l.level {
		return
	}
	data := map[string]interface{}{
		KeyTime:    now(),
		KeyLevel:   level.String(),
		KeyMessage: msg,
	}
	if _, file, line, ok := runtime.Caller(calldepth); ok {
		data[KeyFile] = fmt.Sprintf("%s:%d", path.Base(file), line)
	}
	for k, v := range fields {
		data[k] = v
	}
	for k, v := range l.fields {
		data[k] = v
	}
	select {
	case l.output.input <- data:
		// Sent with success
	default:
		// Channel is full, message dropped
	}
}

func extractFields(v *[]interface{}) map[string]interface{} {
	if l := len(*v); l > 0 {
		if f, ok := (*v)[l-1].(map[string]interface{}); ok {
			*v = (*v)[:l-1]
			return f
		}
		if f, ok := (*v)[l-1].(F); ok {
			*v = (*v)[:l-1]
			return f
		}
	}
	return nil
}

// SetField implements Logger interface
func (l *logger) SetField(name string, value interface{}) {
	if l.fields == nil {
		l.fields = map[string]interface{}{}
	}
	l.fields[name] = value
}

// Debug implements Logger interface
func (l *logger) Debug(v ...interface{}) {
	f := extractFields(&v)
	l.send(LevelDebug, 2, fmt.Sprint(v...), f)
}

// Debugf implements Logger interface
func (l *logger) Debugf(format string, v ...interface{}) {
	f := extractFields(&v)
	l.send(LevelDebug, 2, fmt.Sprintf(format, v...), f)
}

// Info implements Logger interface
func (l *logger) Info(v ...interface{}) {
	f := extractFields(&v)
	l.send(LevelInfo, 2, fmt.Sprint(v...), f)
}

// Infof implements Logger interface
func (l *logger) Infof(format string, v ...interface{}) {
	f := extractFields(&v)
	l.send(LevelInfo, 2, fmt.Sprintf(format, v...), f)
}

// Warn implements Logger interface
func (l *logger) Warn(v ...interface{}) {
	f := extractFields(&v)
	l.send(LevelWarn, 2, fmt.Sprint(v...), f)
}

// Warnf implements Logger interface
func (l *logger) Warnf(format string, v ...interface{}) {
	f := extractFields(&v)
	l.send(LevelWarn, 2, fmt.Sprintf(format, v...), f)
}

// Error implements Logger interface
func (l *logger) Error(v ...interface{}) {
	f := extractFields(&v)
	l.send(LevelError, 2, fmt.Sprint(v...), f)
}

// Errorf implements Logger interface
//
// Go vet users: you may append %v at the end of you format when using xlog.F{} as a last
// argument to workaround go vet false alarm.
func (l *logger) Errorf(format string, v ...interface{}) {
	f := extractFields(&v)
	if f != nil {
		// Let user add a %v at the end of the message when fields are passed to satisfy go vet
		l := len(format)
		if l > 2 && format[l-2] == '%' && format[l-1] == 'v' {
			format = format[0 : l-2]
		}
	}
	l.send(LevelError, 2, fmt.Sprintf(format, v...), f)
}

// Fatal implements Logger interface
func (l *logger) Fatal(v ...interface{}) {
	f := extractFields(&v)
	l.send(LevelError, 2, fmt.Sprint(v...), f)
	exit1()
}

// Fatalf implements Logger interface
//
// Go vet users: you may append %v at the end of you format when using xlog.F{} as a last
// argument to workaround go vet false alarm.
func (l *logger) Fatalf(format string, v ...interface{}) {
	f := extractFields(&v)
	if f != nil {
		// Let user add a %v at the end of the message when fields are passed to satisfy go vet
		l := len(format)
		if l > 2 && format[l-2] == '%' && format[l-1] == 'v' {
			format = format[0 : l-2]
		}
	}
	l.send(LevelError, 2, fmt.Sprintf(format, v...), f)
	exit1()
}

// Write implements io.Writer interface
func (l *logger) Write(p []byte) (int, error) {
	msg := strings.TrimRight(string(p), "\n")
	l.send(LevelInfo, 4, msg, nil)
	return len(p), nil
}
<commit_msg>Set default output on xlog.New()<commit_after>// Package xlog is a logger coupled with HTTP net/context aware middleware.
//
// Features:
//
//   - Per request log context
//   - Per request and/or per message key/value fields
//   - Log levels (Debug, Info, Warn, Error)
//   - Custom output
//   - Automatically gathers request context like User-Agent, IP etc.
//   - Drops message rather than blocking execution
//
// It works best in combination with github.com/rs/xhandler.
package xlog // import "github.com/rs/xlog"

import (
	"fmt"
	"io"
	"os"
	"path"
	"runtime"
	"strings"
	"sync"
	"time"
)

// Logger is per request logger interface
type Logger interface {
	// Implements io.Writer so it can be set a output of log.Logger
	io.Writer

	// SetField sets a field on the logger's context. All future messages on this logger
	// will have this field set.
	SetField(name string, value interface{})
	// Debug logs a debug message. If last parameter is a map[string]string, it's content
	// is added as fields to the message.
	Debug(v ...interface{})
	// Debug logs a debug message with format. If last parameter is a map[string]string,
	// it's content is added as fields to the message.
	Debugf(format string, v ...interface{})
	// Info logs a info message. If last parameter is a map[string]string, it's content
	// is added as fields to the message.
	Info(v ...interface{})
	// Info logs a info message with format. If last parameter is a map[string]string,
	// it's content is added as fields to the message.
	Infof(format string, v ...interface{})
	// Warn logs a warning message. If last parameter is a map[string]string, it's content
	// is added as fields to the message.
	Warn(v ...interface{})
	// Warn logs a warning message with format. If last parameter is a map[string]string,
	// it's content is added as fields to the message.
	Warnf(format string, v ...interface{})
	// Error logs an error message. If last parameter is a map[string]string, it's content
	// is added as fields to the message.
	Error(v ...interface{})
	// Error logs an error message with format. If last parameter is a map[string]string,
	// it's content is added as fields to the message.
	Errorf(format string, v ...interface{})
	// Fatal logs an error message followed by a call to os.Exit(1). If last parameter is a
	// map[string]string, it's content is added as fields to the message.
	Fatal(v ...interface{})
	// Fatalf logs an error message with format followed by a call to ox.Exit(1). If last
	// parameter is a map[string]string, it's content is added as fields to the message.
	Fatalf(format string, v ...interface{})
}

// Config defines logger's config
type Config struct {
	// Level is the maximum level to output, logs with lower level are discarded
	Level Level
	// Fields defines default fields to use with all messages
	Fields map[string]interface{}
	// Output is the channel to use to write log messages to
	Output *OutputChannel
}

// F represents a set of log message fields string -> interface{}
type F map[string]interface{}

type logger struct {
	level  Level
	output *OutputChannel
	fields map[string]interface{}
}

// Common key names for log messages
const (
	KeyTime    = "time"
	KeyMessage = "message"
	KeyLevel   = "level"
	KeyFile    = "file"
)

var now = time.Now
var exit1 = func() { os.Exit(1) }

var loggerPool = sync.Pool{
	New: func() interface{} {
		return &logger{}
	},
}

// New manually creates a logger.
//
// This function should only be used out of a request. Use FromContext in request.
func New(c Config) Logger {
	l := loggerPool.Get().(*logger)
	l.level = c.Level
	l.output = c.Output
	if l.output == nil {
		l.output = NewOutputChannel(NewConsoleOutput())
	}
	for k, v := range c.Fields {
		l.SetField(k, v)
	}
	return l
}

func (l *logger) close() {
	l.level = 0
	l.output = nil
	l.fields = nil
	loggerPool.Put(l)
}

func (l *logger) send(level Level, calldepth int, msg string, fields map[string]interface{}) {
	if level < l.level {
		return
	}
	data := map[string]interface{}{
		KeyTime:    now(),
		KeyLevel:   level.String(),
		KeyMessage: msg,
	}
	if _, file, line, ok := runtime.Caller(calldepth); ok {
		data[KeyFile] = fmt.Sprintf("%s:%d", path.Base(file), line)
	}
	for k, v := range fields {
		data[k] = v
	}
	for k, v := range l.fields {
		data[k] = v
	}
	select {
	case l.output.input <- data:
		// Sent with success
	default:
		// Channel is full, message dropped
	}
}

func extractFields(v *[]interface{}) map[string]interface{} {
	if l := len(*v); l > 0 {
		if f, ok := (*v)[l-1].(map[string]interface{}); ok {
			*v = (*v)[:l-1]
			return f
		}
		if f, ok := (*v)[l-1].(F); ok {
			*v = (*v)[:l-1]
			return f
		}
	}
	return nil
}

// SetField implements Logger interface
func (l *logger) SetField(name string, value interface{}) {
	if l.fields == nil {
		l.fields = map[string]interface{}{}
	}
	l.fields[name] = value
}

// Debug implements Logger interface
func (l *logger) Debug(v ...interface{}) {
	f := extractFields(&v)
	l.send(LevelDebug, 2, fmt.Sprint(v...), f)
}

// Debugf implements Logger interface
func (l *logger) Debugf(format string, v ...interface{}) {
	f := extractFields(&v)
	l.send(LevelDebug, 2, fmt.Sprintf(format, v...), f)
}

// Info implements Logger interface
func (l *logger) Info(v ...interface{}) {
	f := extractFields(&v)
	l.send(LevelInfo, 2, fmt.Sprint(v...), f)
}

// Infof implements Logger interface
func (l *logger) Infof(format string, v ...interface{}) {
	f := extractFields(&v)
	l.send(LevelInfo, 2, fmt.Sprintf(format, v...), f)
}

// Warn implements Logger interface
func (l *logger) Warn(v ...interface{}) {
	f := extractFields(&v)
	l.send(LevelWarn, 2, fmt.Sprint(v...), f)
}

// Warnf implements Logger interface
func (l *logger) Warnf(format string, v ...interface{}) {
	f := extractFields(&v)
	l.send(LevelWarn, 2, fmt.Sprintf(format, v...), f)
}

// Error implements Logger interface
func (l *logger) Error(v ...interface{}) {
	f := extractFields(&v)
	l.send(LevelError, 2, fmt.Sprint(v...), f)
}

// Errorf implements Logger interface
//
// Go vet users: you may append %v at the end of you format when using xlog.F{} as a last
// argument to workaround go vet false alarm.
func (l *logger) Errorf(format string, v ...interface{}) {
	f := extractFields(&v)
	if f != nil {
		// Let user add a %v at the end of the message when fields are passed to satisfy go vet
		l := len(format)
		if l > 2 && format[l-2] == '%' && format[l-1] == 'v' {
			format = format[0 : l-2]
		}
	}
	l.send(LevelError, 2, fmt.Sprintf(format, v...), f)
}

// Fatal implements Logger interface
func (l *logger) Fatal(v ...interface{}) {
	f := extractFields(&v)
	l.send(LevelError, 2, fmt.Sprint(v...), f)
	exit1()
}

// Fatalf implements Logger interface
//
// Go vet users: you may append %v at the end of you format when using xlog.F{} as a last
// argument to workaround go vet false alarm.
func (l *logger) Fatalf(format string, v ...interface{}) {
	f := extractFields(&v)
	if f != nil {
		// Let user add a %v at the end of the message when fields are passed to satisfy go vet
		l := len(format)
		if l > 2 && format[l-2] == '%' && format[l-1] == 'v' {
			format = format[0 : l-2]
		}
	}
	l.send(LevelError, 2, fmt.Sprintf(format, v...), f)
	exit1()
}

// Write implements io.Writer interface
func (l *logger) Write(p []byte) (int, error) {
	msg := strings.TrimRight(string(p), "\n")
	l.send(LevelInfo, 4, msg, nil)
	return len(p), nil
}
<|endoftext|>


<commit_before>// Package zest contains a lightweight framework based on the codegangsta/cli package allowing
// clean and easy command line interfaces, the codegangsta/negroni middleware
// handler, and the solher/syringe injector.
//
// Zest encourages the use of small, well chosen individual dependencies
// instead of high productivity, full-stack frameworks.
package zest

import (
	"fmt"
	"os"
	"time"

	"gopkg.in/tylerb/graceful.v1"

	"github.com/codegangsta/cli"
	"github.com/codegangsta/negroni"
	"github.com/go-zoo/bone"
	"github.com/rs/cors"
	"github.com/solher/syringe"
)

// Injector provide a quick access to an instanciated injector.
var Injector = syringe.New()

// SeqFunc is the prototype of the functions present in the launch/exit sequences.
type SeqFunc func(z *Zest) error

// Zest is an aggregation of well known and efficient package, also providing
// a simple launch/exit process to the user.
//
// The launch sequence is divided into three steps:
// - The register sequence is run, allowing the user to register dependencies
// into the injector.
// - The injection is run.
// - The init sequence is run, allowing the user to properly initialize the
// freshly built app.
// Launch and exit sequences are run following the order of the arrays, at each
// start/stop of the app, thanks to Cli and the tylerb/graceful module.
type Zest struct {
	cli     *cli.App
	Context *cli.Context

	Server   *negroni.Negroni
	Injector *syringe.Syringe

	RegisterSequence []SeqFunc
	InitSequence     []SeqFunc
	ExitSequence     []SeqFunc
}

// Cli returns a copy of the embedded Cli app.
func (z *Zest) Cli() cli.App {
	return *z.cli
}

// SetCli sets a copy of the embedded Cli app.
func (z *Zest) SetCli(cli cli.App) {
	*z.cli = cli
}

// Run starts the Cli app.
func (z *Zest) Run() {
	z.cli.Run(os.Args)
}

// Classic returns a new instance of Zest, with some default register and init steps:
// "classicRegister" which registers the default dependencies (Render, Bone) in the injector.
// "classicInit" which initialize the Bone router and the default middlewares in Negroni.
func Classic() *Zest {
	z := New()

	z.RegisterSequence = append(z.RegisterSequence, classicRegister)
	z.InitSequence = append(z.InitSequence, classicInit)

	return z
}

// New returns a new instance of Zest.
func New() *Zest {
	z := &Zest{
		cli:      cli.NewApp(),
		Server:   negroni.New(),
		Injector: Injector,
	}

	z.cli.Usage = "A Zest powered service."
	z.cli.Before = z.init
	z.cli.After = z.exit
	z.cli.Action = z.run
	z.cli.Flags = []cli.Flag{
		cli.IntFlag{
			Name:  "port,p",
			Value: 3000,
			Usage: "listening port",
		},
		cli.IntFlag{
			Name:  "exitTimeout,t",
			Value: 10,
			Usage: "graceful shutdown timeout in seconds (0 for infinite)",
		},
	}

	return z
}

func (z *Zest) init(c *cli.Context) error {
	z.Context = c

	for _, f := range z.RegisterSequence {
		if err := f(z); err != nil {
			return err
		}
	}

	z.Injector.Inject()

	for _, f := range z.InitSequence {
		if err := f(z); err != nil {
			return err
		}
	}

	return nil
}

func (z *Zest) run(c *cli.Context) {
	z.Context = c

	port := fmt.Sprintf(":%d", z.Context.GlobalInt("port"))
	exitTimeout := time.Duration(z.Context.GlobalInt("exitTimeout")) * time.Second

	fmt.Println("\n[Zest] Listening on " + port)

	graceful.Run(port, exitTimeout, z.Server)
}

func (z *Zest) exit(c *cli.Context) error {
	z.Context = c

	for _, f := range z.ExitSequence {
		if err := f(z); err != nil {
			return err
		}
	}

	return nil
}

func classicRegister(z *Zest) error {
	z.Injector.Register(NewRender(), bone.New())

	return nil
}

func classicInit(z *Zest) error {
	d := &struct{ Router *bone.Mux }{}

	if err := z.Injector.Get(d); err != nil {
		return err
	}

	z.Server.Use(NewRecovery())
	z.Server.Use(NewLogger())
	z.Server.Use(cors.Default())

	z.Server.UseHandler(d.Router)

	return nil
}
<commit_msg>Env vars added in CLI.<commit_after>// Package zest contains a lightweight framework based on the codegangsta/cli package allowing
// clean and easy command line interfaces, the codegangsta/negroni middleware
// handler, and the solher/syringe injector.
//
// Zest encourages the use of small, well chosen individual dependencies
// instead of high productivity, full-stack frameworks.
package zest

import (
	"fmt"
	"os"
	"time"

	"gopkg.in/tylerb/graceful.v1"

	"github.com/codegangsta/cli"
	"github.com/codegangsta/negroni"
	"github.com/go-zoo/bone"
	"github.com/rs/cors"
	"github.com/solher/syringe"
)

// Injector provide a quick access to an instanciated injector.
var Injector = syringe.New()

// SeqFunc is the prototype of the functions present in the launch/exit sequences.
type SeqFunc func(z *Zest) error

// Zest is an aggregation of well known and efficient package, also providing
// a simple launch/exit process to the user.
//
// The launch sequence is divided into three steps:
// - The register sequence is run, allowing the user to register dependencies
// into the injector.
// - The injection is run.
// - The init sequence is run, allowing the user to properly initialize the
// freshly built app.
// Launch and exit sequences are run following the order of the arrays, at each
// start/stop of the app, thanks to Cli and the tylerb/graceful module.
type Zest struct {
	cli     *cli.App
	Context *cli.Context

	Server   *negroni.Negroni
	Injector *syringe.Syringe

	RegisterSequence []SeqFunc
	InitSequence     []SeqFunc
	ExitSequence     []SeqFunc
}

// Cli returns a copy of the embedded Cli app.
func (z *Zest) Cli() cli.App {
	return *z.cli
}

// SetCli sets a copy of the embedded Cli app.
func (z *Zest) SetCli(cli cli.App) {
	*z.cli = cli
}

// Run starts the Cli app.
func (z *Zest) Run() {
	z.cli.Run(os.Args)
}

// Classic returns a new instance of Zest, with some default register and init steps:
// "classicRegister" which registers the default dependencies (Render, Bone) in the injector.
// "classicInit" which initialize the Bone router and the default middlewares in Negroni.
func Classic() *Zest {
	z := New()

	z.RegisterSequence = append(z.RegisterSequence, classicRegister)
	z.InitSequence = append(z.InitSequence, classicInit)

	return z
}

// New returns a new instance of Zest.
func New() *Zest {
	z := &Zest{
		cli:      cli.NewApp(),
		Server:   negroni.New(),
		Injector: Injector,
	}

	z.cli.Usage = "A Zest powered service."
	z.cli.Before = z.init
	z.cli.After = z.exit
	z.cli.Action = z.run
	z.cli.Flags = []cli.Flag{
		cli.IntFlag{
			Name:   "port,p",
			Value:  3000,
			Usage:  "listening port",
			EnvVar: "ZEST_PORT",
		},
		cli.IntFlag{
			Name:   "exitTimeout,t",
			Value:  10,
			Usage:  "graceful shutdown timeout in seconds (0 for infinite)",
			EnvVar: "ZEST_TIMEOUT",
		},
	}

	return z
}

func (z *Zest) init(c *cli.Context) error {
	z.Context = c

	for _, f := range z.RegisterSequence {
		if err := f(z); err != nil {
			return err
		}
	}

	z.Injector.Inject()

	for _, f := range z.InitSequence {
		if err := f(z); err != nil {
			return err
		}
	}

	return nil
}

func (z *Zest) run(c *cli.Context) {
	z.Context = c

	port := fmt.Sprintf(":%d", z.Context.GlobalInt("port"))
	exitTimeout := time.Duration(z.Context.GlobalInt("exitTimeout")) * time.Second

	fmt.Println("\n[Zest] Listening on " + port)

	graceful.Run(port, exitTimeout, z.Server)
}

func (z *Zest) exit(c *cli.Context) error {
	z.Context = c

	for _, f := range z.ExitSequence {
		if err := f(z); err != nil {
			return err
		}
	}

	return nil
}

func classicRegister(z *Zest) error {
	z.Injector.Register(NewRender(), bone.New())

	return nil
}

func classicInit(z *Zest) error {
	d := &struct{ Router *bone.Mux }{}

	if err := z.Injector.Get(d); err != nil {
		return err
	}

	z.Server.Use(NewRecovery())
	z.Server.Use(NewLogger())
	z.Server.Use(cors.Default())

	z.Server.UseHandler(d.Router)

	return nil
}
<|endoftext|>


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

package dns

import (
	"os"
	"sort"
	"strings"
)

const _CLASS = 2 << 16

// ZRRset is a structure that keeps several items from
// a zone file together. It  helps in speeding up DNSSEC lookups.
type ZRRset struct {
	RRs    RRset // the RRset for this type and name
	RRsigs RRset // the RRSIGs belonging to this RRset (if any)
	Nxt    RR    // the NSEC or NSEC3 for this name
	Glue   bool  // when this RRset is glue, set to true
}

func NewZRRset() *ZRRset {
	s := new(ZRRset)
	s.RRs = NewRRset()
	s.RRsigs = NewRRset()
	return s
}

// Zone implements the concept of RFC 1035 master zone files.
// This will be converted to some kind of tree structure
// type Zone map[string]map[int]*ZRRset

// Zone implements the concept of RFC 1035 master zone files.
// We store the zone contents in a map where the ownername is
// the key. In that map we have another map with integers
// (class * _CLASS + type) that has the RRset:
// map[<ownername>] -> map[<int>] -> ZRRset
type Zone struct {
	Zone map[string]map[int]*ZRRset // the contents of the zone
	Nxt  *QnameString               // sorted list of owernames in the zone
}

// NewZone returns a new *Zone
func NewZone() *Zone {
	z := new(Zone)
	z.Zone = make(map[string]map[int]*ZRRset)
	z.Nxt = NewQnameString()
	return z
}

// Get the first value
func (z *Zone) Pop() *ZRRset {
	for _, v := range z.Zone {
		for _, v1 := range v {
			return v1
		}
	}
	return nil
}

func (z *Zone) PopRR() RR {
	s := z.Pop()
	if s == nil {
		return nil
	}
	switch {
	case len(s.RRs) != 0:
		return s.RRs.Pop()
	case len(s.RRsigs) != 0:
		return s.RRsigs.Pop()
	case s.Nxt != nil:
		return s.Nxt
	}
	panic("not reached")
	return nil
}

func (z *Zone) Len() int {
	i := 0
	for _, im := range z.Zone {
		for _, s := range im {
			i += len(s.RRs) + len(s.RRsigs)
			if s.Nxt != nil {
				i++
			}
		}
	}
	return i
}

func (z *Zone) String() string {
	s := ""
	for _, im := range z.Zone {
		for _, s1 := range im {
			s += s1.RRs.String()
			s += s1.RRsigs.String()
			if s1.Nxt != nil {
				s += s1.Nxt.String() + "\n"
			}
		}
	}
	return s
}

// Add a new RR to the zone. First we need to find out if the
// RR already lives inside it.
func (z *Zone) PushRR(r RR) {
	s, _ := z.LookupRR(r)
	if s == nil {
		s = NewZRRset()
	}
	// Add to the sorted ownernames list
	SortInsert(z.Nxt, r.Header().Name)

	switch r.Header().Rrtype {
	case TypeRRSIG:
		s.RRsigs.Push(r)
	case TypeNSEC, TypeNSEC3:
		s.Nxt = r
	default:
		s.RRs.Push(r)
	}
	z.Push(s)
}

// Push a new ZRRset to the zone
func (z *Zone) Push(s *ZRRset) {
	// s can hold RRs, RRsigs or a Nxt
	name := ""
	i := 0
	switch {
	case len(s.RRs) != 0:
		name = s.RRs[0].Header().Name
		i = intval(s.RRs[0].Header().Class, s.RRs[0].Header().Rrtype)
	case len(s.RRsigs) != 0:
		name = s.RRsigs[0].Header().Name
		i = intval(s.RRsigs[0].Header().Class, s.RRsigs[0].(*RR_RRSIG).TypeCovered)
	case s.Nxt != nil:
		name = s.Nxt.Header().Name
		i = intval(s.Nxt.Header().Class, s.Nxt.Header().Rrtype)
	}
	if z.Zone[name] == nil {
		im := make(map[int]*ZRRset) // intmap
		im[i] = s
		z.Zone[name] = im
		return
	}
	im := z.Zone[name]
	im[i] = s
	return
}

// Lookup the RR in the zone, we are only looking at
// qname, qtype and qclass of the RR
// Considerations for wildcards
// Return NXDomain, Name error, wildcard?
// Casing!
func (z *Zone) LookupRR(r RR) (*ZRRset, os.Error) {
	if r.Header().Rrtype == TypeRRSIG {
		return z.LookupName(r.Header().Name, r.Header().Class, r.(*RR_RRSIG).TypeCovered)
	}
	return z.LookupName(r.Header().Name, r.Header().Class, r.Header().Rrtype)
}

func (z *Zone) LookupQuestion(q Question) (*ZRRset, os.Error) {
	// Impossible to look for an typecovered in a question, because the rdata is
	// not there.
	return z.LookupName(q.Name, q.Qclass, q.Qtype)
}

func (z *Zone) LookupName(qname string, qclass, qtype uint16) (*ZRRset, os.Error) {
	i := intval(qclass, qtype)
	if im, ok := z.Zone[strings.ToLower(qname)]; ok {
		// Have an im, intmap
		if s, ok := im[i]; ok {
			return s, nil
		}
		// Wildcard 'n stuff
		return nil, ErrName
	}
	return nil, nil
}

// Number in the second map denotes the class + type.
func intval(c, t uint16) int {
	return int(c)*_CLASS + int(t)
}

// SortInsert insert the string s in the already sorted
// vector p. If s is already present it is not inserted again.
func SortInsert(p *QnameString, s string) {
	i := sort.Search(len(*p), func(i int) bool { return (*p)[i] >= s })
	if i < len(*p) && (*p)[i] == s {
		// element already there
		return
	}
	p.Insert(i, s)
}

// Search searches the sorted vector p using binary search. If
// the element s can not be found, the previous element is returned.
func SortSearch(p *QnameString, s string) string {
	i := sort.Search(len(*p), func(i int) bool { return (*p)[i] >= s })
	// with zones there must always be one before
	if (*p)[i] == s {
		return s
	}
	if i > 0 {
		i--
	}
	return (*p)[i]
}
<commit_msg>documentation<commit_after>// Copyright 2011 Miek Gieben. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package dns

import (
	"os"
	"sort"
	"strings"
)

const _CLASS = 2 << 16

// ZRRset is a structure that keeps several items from
// a zone file together.
type ZRRset struct {
	RRs    RRset // the RRset for this type and name
	RRsigs RRset // the RRSIGs belonging to this RRset (if any)
	Nxt    RR    // the NSEC or NSEC3 for this name
	Glue   bool  // when this RRset is glue, set to true
}

// NewZRRset returns a pointer to a new ZRRset.
func NewZRRset() *ZRRset {
	s := new(ZRRset)
	s.RRs = NewRRset()
	s.RRsigs = NewRRset()
	return s
}

// Zone implements the concept of RFC 1035 master zone files.
// We store the zone contents in a map where the ownername is
// the key. In that map we have another map with integers
// (class * _CLASS + type) that has the ZRRset:
// map[<ownername>] -> map[<int>] -> ZRRset
type Zone struct {
	Zone map[string]map[int]*ZRRset // the contents of the zone
	Nxt  *QnameString               // sorted list of owernames in the zone
}

// NewZone returns a new *Zone
func NewZone() *Zone {
	z := new(Zone)
	z.Zone = make(map[string]map[int]*ZRRset)
	z.Nxt = NewQnameString()
	return z
}

// Pop returns the last pushed ZRRset from z.
// Get the first value
func (z *Zone) Pop() *ZRRset {
	for _, v := range z.Zone {
		for _, v1 := range v {
			return v1
		}
	}
	return nil
}

// PopRR returns the last RR pushed from z.
func (z *Zone) PopRR() RR {
	s := z.Pop()
	if s == nil {
		return nil
	}
	switch {
	case len(s.RRs) != 0:
		return s.RRs.Pop()
	case len(s.RRsigs) != 0:
		return s.RRsigs.Pop()
	case s.Nxt != nil:
		return s.Nxt
	}
	panic("not reached")
	return nil
}

// Len returns the number of RRs in z.
func (z *Zone) Len() int {
	i := 0
	for _, im := range z.Zone {
		for _, s := range im {
			i += len(s.RRs) + len(s.RRsigs)
			if s.Nxt != nil {
				i++
			}
		}
	}
	return i
}

func (z *Zone) String() string {
	s := ""
	for _, im := range z.Zone {
		for _, s1 := range im {
			s += s1.RRs.String()
			s += s1.RRsigs.String()
			if s1.Nxt != nil {
				s += s1.Nxt.String() + "\n"
			}
		}
	}
	return s
}

// PushRR adds a new RR to the zone.
func (z *Zone) PushRR(r RR) {
	s, _ := z.LookupRR(r)
	if s == nil {
		s = NewZRRset()
	}
	// Add to the sorted ownernames list
	SortInsert(z.Nxt, r.Header().Name)

	switch r.Header().Rrtype {
	case TypeRRSIG:
		s.RRsigs.Push(r)
	case TypeNSEC, TypeNSEC3:
		s.Nxt = r
	default:
		s.RRs.Push(r)
	}
	z.Push(s)
}

// Push adds a new ZRRset to the zone.
func (z *Zone) Push(s *ZRRset) {
	// s can hold RRs, RRsigs or a Nxt
	name := ""
	i := 0
	switch {
	case len(s.RRs) != 0:
		name = s.RRs[0].Header().Name
		i = intval(s.RRs[0].Header().Class, s.RRs[0].Header().Rrtype)
	case len(s.RRsigs) != 0:
		name = s.RRsigs[0].Header().Name
		i = intval(s.RRsigs[0].Header().Class, s.RRsigs[0].(*RR_RRSIG).TypeCovered)
	case s.Nxt != nil:
		name = s.Nxt.Header().Name
		i = intval(s.Nxt.Header().Class, s.Nxt.Header().Rrtype)
	}
	if z.Zone[name] == nil {
		im := make(map[int]*ZRRset) // intmap
		im[i] = s
		z.Zone[name] = im
		return
	}
	im := z.Zone[name]
	im[i] = s
	return
}

// Lookup the RR in the zone, we are only looking at
// qname, qtype and qclass of the RR
// Considerations for wildcards
// Return NXDomain, Name error, wildcard?
// Casing!
func (z *Zone) LookupRR(r RR) (*ZRRset, os.Error) {
	if r.Header().Rrtype == TypeRRSIG {
		return z.LookupName(r.Header().Name, r.Header().Class, r.(*RR_RRSIG).TypeCovered)
	}
	return z.LookupName(r.Header().Name, r.Header().Class, r.Header().Rrtype)
}

func (z *Zone) LookupQuestion(q Question) (*ZRRset, os.Error) {
	// Impossible to look for an typecovered in a question, because the rdata is
	// not there.
	return z.LookupName(q.Name, q.Qclass, q.Qtype)
}

func (z *Zone) LookupName(qname string, qclass, qtype uint16) (*ZRRset, os.Error) {
	i := intval(qclass, qtype)
	if im, ok := z.Zone[strings.ToLower(qname)]; ok {
		// Have an im, intmap
		if s, ok := im[i]; ok {
			return s, nil
		}
		// Wildcard 'n stuff
		return nil, ErrName
	}
	return nil, nil
}

// Number in the second map denotes the class + type.
func intval(c, t uint16) int {
	return int(c)*_CLASS + int(t)
}

// SortInsert insert the string s in the already sorted
// vector p. If s is already present it is not inserted again.
func SortInsert(p *QnameString, s string) {
	i := sort.Search(len(*p), func(i int) bool { return (*p)[i] >= s })
	if i < len(*p) && (*p)[i] == s {
		// element already there
		return
	}
	p.Insert(i, s)
}

// Search searches the sorted vector p using binary search. If
// the element s can not be found, the previous element is returned.
func SortSearch(p *QnameString, s string) string {
	i := sort.Search(len(*p), func(i int) bool { return (*p)[i] >= s })
	// with zones there must always be one before
	if (*p)[i] == s {
		return s
	}
	if i > 0 {
		i--
	}
	return (*p)[i]
}
<|endoftext|>


<commit_before>package ledis

import (
	"fmt"
	"github.com/siddontang/go/filelock"
	"github.com/siddontang/go/log"
	"github.com/siddontang/ledisdb/config"
	"github.com/siddontang/ledisdb/rpl"
	"github.com/siddontang/ledisdb/store"
	"io"
	"os"
	"path"
	"sync"
	"time"
)

type Ledis struct {
	cfg *config.Config

	ldb *store.DB

	dbLock sync.Mutex
	dbs    map[int]*DB

	quit chan struct{}
	wg   sync.WaitGroup

	//for replication
	r      *rpl.Replication
	rc     chan struct{}
	rbatch *store.WriteBatch
	rwg    sync.WaitGroup
	rhs    []NewLogEventHandler

	wLock      sync.RWMutex //allow one write at same time
	commitLock sync.Mutex   //allow one write commit at same time

	lock io.Closer

	ttlCheckers  []*ttlChecker
	ttlCheckerCh chan *ttlChecker
}

func Open(cfg *config.Config) (*Ledis, error) {
	if len(cfg.DataDir) == 0 {
		cfg.DataDir = config.DefaultDataDir
	}

	if cfg.Databases == 0 {
		cfg.Databases = 16
	} else if cfg.Databases > MaxDatabases {
		cfg.Databases = MaxDatabases
	}

	os.MkdirAll(cfg.DataDir, 0755)

	var err error

	l := new(Ledis)
	l.cfg = cfg

	if l.lock, err = filelock.Lock(path.Join(cfg.DataDir, "LOCK")); err != nil {
		return nil, err
	}

	l.quit = make(chan struct{})

	if l.ldb, err = store.Open(cfg); err != nil {
		return nil, err
	}

	if cfg.UseReplication {
		if l.r, err = rpl.NewReplication(cfg); err != nil {
			return nil, err
		}

		l.rc = make(chan struct{}, 1)
		l.rbatch = l.ldb.NewWriteBatch()

		l.wg.Add(1)
		go l.onReplication()

		//first we must try wait all replication ok
		//maybe some logs are not committed
		l.WaitReplication()
	} else {
		l.r = nil
	}

	l.dbs = make(map[int]*DB, 16)

	l.checkTTL()

	return l, nil
}

func (l *Ledis) Close() {
	close(l.quit)
	l.wg.Wait()

	l.ldb.Close()

	if l.r != nil {
		l.r.Close()
		//l.r = nil
	}

	if l.lock != nil {
		l.lock.Close()
		//l.lock = nil
	}
}

func (l *Ledis) Select(index int) (*DB, error) {
	if index < 0 || index >= l.cfg.Databases {
		return nil, fmt.Errorf("invalid db index %d, must in [0, %d]", index, l.cfg.Databases-1)
	}

	l.dbLock.Lock()
	defer l.dbLock.Unlock()

	db, ok := l.dbs[index]
	if ok {
		return db, nil
	}

	db = l.newDB(index)
	l.dbs[index] = db

	go func(db *DB) {
		l.ttlCheckerCh <- db.ttlChecker
	}(db)

	return db, nil
}

// Flush All will clear all data and replication logs
func (l *Ledis) FlushAll() error {
	l.wLock.Lock()
	defer l.wLock.Unlock()

	return l.flushAll()
}

func (l *Ledis) flushAll() error {
	it := l.ldb.NewIterator()
	defer it.Close()

	it.SeekToFirst()

	w := l.ldb.NewWriteBatch()
	defer w.Rollback()

	n := 0
	for ; it.Valid(); it.Next() {
		n++
		if n == 10000 {
			if err := w.Commit(); err != nil {
				log.Fatalf("flush all commit error: %s", err.Error())
				return err
			}
			n = 0
		}
		w.Delete(it.RawKey())
	}

	if err := w.Commit(); err != nil {
		log.Fatalf("flush all commit error: %s", err.Error())
		return err
	}

	if l.r != nil {
		if err := l.r.Clear(); err != nil {
			log.Fatalf("flush all replication clear error: %s", err.Error())
			return err
		}
	}

	return nil
}

func (l *Ledis) IsReadOnly() bool {
	if l.cfg.GetReadonly() {
		return true
	} else if l.r != nil {
		if b, _ := l.r.CommitIDBehind(); b {
			return true
		}
	}
	return false
}

func (l *Ledis) checkTTL() {
	l.ttlCheckers = make([]*ttlChecker, 0, 16)
	l.ttlCheckerCh = make(chan *ttlChecker, 16)

	if l.cfg.TTLCheckInterval == 0 {
		l.cfg.TTLCheckInterval = 1
	}

	l.wg.Add(1)
	go func() {
		defer l.wg.Done()

		tick := time.NewTicker(time.Duration(l.cfg.TTLCheckInterval) * time.Second)
		defer tick.Stop()

		for {
			select {
			case <-tick.C:
				if l.IsReadOnly() {
					break
				}

				for _, c := range l.ttlCheckers {
					c.check()
				}
			case c := <-l.ttlCheckerCh:
				l.ttlCheckers = append(l.ttlCheckers, c)
				c.check()
			case <-l.quit:
				return
			}
		}

	}()

}

func (l *Ledis) StoreStat() *store.Stat {
	return l.ldb.Stat()
}
<commit_msg>add CompactStore interface<commit_after>package ledis

import (
	"fmt"
	"github.com/siddontang/go/filelock"
	"github.com/siddontang/go/log"
	"github.com/siddontang/ledisdb/config"
	"github.com/siddontang/ledisdb/rpl"
	"github.com/siddontang/ledisdb/store"
	"io"
	"os"
	"path"
	"sync"
	"time"
)

type Ledis struct {
	cfg *config.Config

	ldb *store.DB

	dbLock sync.Mutex
	dbs    map[int]*DB

	quit chan struct{}
	wg   sync.WaitGroup

	//for replication
	r      *rpl.Replication
	rc     chan struct{}
	rbatch *store.WriteBatch
	rwg    sync.WaitGroup
	rhs    []NewLogEventHandler

	wLock      sync.RWMutex //allow one write at same time
	commitLock sync.Mutex   //allow one write commit at same time

	lock io.Closer

	ttlCheckers  []*ttlChecker
	ttlCheckerCh chan *ttlChecker
}

func Open(cfg *config.Config) (*Ledis, error) {
	if len(cfg.DataDir) == 0 {
		cfg.DataDir = config.DefaultDataDir
	}

	if cfg.Databases == 0 {
		cfg.Databases = 16
	} else if cfg.Databases > MaxDatabases {
		cfg.Databases = MaxDatabases
	}

	os.MkdirAll(cfg.DataDir, 0755)

	var err error

	l := new(Ledis)
	l.cfg = cfg

	if l.lock, err = filelock.Lock(path.Join(cfg.DataDir, "LOCK")); err != nil {
		return nil, err
	}

	l.quit = make(chan struct{})

	if l.ldb, err = store.Open(cfg); err != nil {
		return nil, err
	}

	if cfg.UseReplication {
		if l.r, err = rpl.NewReplication(cfg); err != nil {
			return nil, err
		}

		l.rc = make(chan struct{}, 1)
		l.rbatch = l.ldb.NewWriteBatch()

		l.wg.Add(1)
		go l.onReplication()

		//first we must try wait all replication ok
		//maybe some logs are not committed
		l.WaitReplication()
	} else {
		l.r = nil
	}

	l.dbs = make(map[int]*DB, 16)

	l.checkTTL()

	return l, nil
}

func (l *Ledis) Close() {
	close(l.quit)
	l.wg.Wait()

	l.ldb.Close()

	if l.r != nil {
		l.r.Close()
		//l.r = nil
	}

	if l.lock != nil {
		l.lock.Close()
		//l.lock = nil
	}
}

func (l *Ledis) Select(index int) (*DB, error) {
	if index < 0 || index >= l.cfg.Databases {
		return nil, fmt.Errorf("invalid db index %d, must in [0, %d]", index, l.cfg.Databases-1)
	}

	l.dbLock.Lock()
	defer l.dbLock.Unlock()

	db, ok := l.dbs[index]
	if ok {
		return db, nil
	}

	db = l.newDB(index)
	l.dbs[index] = db

	go func(db *DB) {
		l.ttlCheckerCh <- db.ttlChecker
	}(db)

	return db, nil
}

// Flush All will clear all data and replication logs
func (l *Ledis) FlushAll() error {
	l.wLock.Lock()
	defer l.wLock.Unlock()

	return l.flushAll()
}

func (l *Ledis) flushAll() error {
	it := l.ldb.NewIterator()
	defer it.Close()

	it.SeekToFirst()

	w := l.ldb.NewWriteBatch()
	defer w.Rollback()

	n := 0
	for ; it.Valid(); it.Next() {
		n++
		if n == 10000 {
			if err := w.Commit(); err != nil {
				log.Fatalf("flush all commit error: %s", err.Error())
				return err
			}
			n = 0
		}
		w.Delete(it.RawKey())
	}

	if err := w.Commit(); err != nil {
		log.Fatalf("flush all commit error: %s", err.Error())
		return err
	}

	if l.r != nil {
		if err := l.r.Clear(); err != nil {
			log.Fatalf("flush all replication clear error: %s", err.Error())
			return err
		}
	}

	return nil
}

func (l *Ledis) IsReadOnly() bool {
	if l.cfg.GetReadonly() {
		return true
	} else if l.r != nil {
		if b, _ := l.r.CommitIDBehind(); b {
			return true
		}
	}
	return false
}

func (l *Ledis) checkTTL() {
	l.ttlCheckers = make([]*ttlChecker, 0, 16)
	l.ttlCheckerCh = make(chan *ttlChecker, 16)

	if l.cfg.TTLCheckInterval == 0 {
		l.cfg.TTLCheckInterval = 1
	}

	l.wg.Add(1)
	go func() {
		defer l.wg.Done()

		tick := time.NewTicker(time.Duration(l.cfg.TTLCheckInterval) * time.Second)
		defer tick.Stop()

		for {
			select {
			case <-tick.C:
				if l.IsReadOnly() {
					break
				}

				for _, c := range l.ttlCheckers {
					c.check()
				}
			case c := <-l.ttlCheckerCh:
				l.ttlCheckers = append(l.ttlCheckers, c)
				c.check()
			case <-l.quit:
				return
			}
		}

	}()

}

func (l *Ledis) StoreStat() *store.Stat {
	return l.ldb.Stat()
}

func (l *Ledis) CompactStore() error {
	l.wLock.Lock()
	defer l.wLock.Unlock()

	return l.ldb.Compact()
}
<|endoftext|>


<commit_before>package node

import (
	//"fmt"
	//"time"
	//"net/http"
	"errors"
	"context"
	//"google.golang.org/appengine"
	"google.golang.org/appengine/datastore"
	//"strconv"
)

/* == Struct -finder- ============================================== */
type NodeStorage struct{
	context context.Context
	dataTypeList []string
}

type NodeFinder struct{
	Node []Node
}

func (s *NodeFinder) Get() []Node {
	return s.Node
}

func (s *NodeFinder) Title(title string) *NodeFinder {
	var nl []Node

	for _,v := range(s.Node) {
		if v.GetTitle() == title {
			nl = append(nl, v)
		}
	}

	s.Node = nl

	return s
}

//Impliment
//func (s *NodeFinder) FuzzyTitle(title string) *NodeFinder {
//}

func (s *NodeFinder) Description(desc string) *NodeFinder {
	var nl []Node

	for _,v := range(s.Node) {
		if v.GetDescription() == desc {
			nl = append(nl, v)
		}
	}

	s.Node = nl

	return s
}

//Impliment
//func (s *NodeFinder) FuzzyDescription(title string) *NodeFinder {
//}

/* == Struct -Main- ================================================= */
func NewNodeStorage(ctx context.Context) *NodeStorage {
	s := new(NodeStorage)

	s.init(ctx)

	return s
}

func (s *NodeStorage) init(ctx context.Context) {
	s.context = ctx
	s.dataTypeList = []string{
		"NodeURL",
		"NodeText",
	}
}

/* create NodeFinder */
func (s *NodeStorage) Find(dataType string) *NodeFinder {
	nf := new(NodeFinder)

	switch dataType {
	case "NodeURL":
		nf.Node = s.getNodeURL()
	case "NodeText":
		nf.Node = s.getNodeText()
	}

	return nf
}

func (s *NodeStorage) FindByType(dataType string) []Node {
	var ndl []Node

	switch dataType {
	case "NodeURL":
		ndl = s.getNodeURL()
	case "NodeText":
		ndl = s.getNodeText()
	}

	return ndl
}

func (s *NodeStorage) FindByTitle(title string) interface{}{
	var ndl []Node

	//q := datastore.NewQuery("NodeURL").Filter("Title =", title)

	//keys, _ := q.GetAll(s.context, &ndl)
	//for _, v := range(s.dataTypeList) {
	//	q := datastore.NewQuery(v).Filter("Title =", title)

	//	keys, _ := q.GetAll(s.context, &ndl)

	//	if len(keys) > 0 {
	//		return ndl
	//	}
	//}

	return ndl

	//key := datastore.NewQuery()

}

func (s *NodeStorage) FindByName(dataName string, searchType string) {
	//TODO:name ->FindByFuzzyName?
	if searchType == "fuzzy" {
	} else if searchType == "strict" {
	}
}

func (s *NodeStorage) SaveMulti(node []Node) {
	for _, v := range(node) {
		s.Save(v)
	}
}

func (s *NodeStorage) Save(ndi Node) {
	//TODO:name ->newNode/New?
	if ndi.GetID() == 0 {
		key := datastore.NewIncompleteKey(s.context, ndi.GetType(), nil)
		k, _ := datastore.Put(s.context, key, ndi)

		id := k.IntID()
		datastore.Delete(s.context, key)
		datastore.Delete(s.context, k)

		ndi.SetID(id)
		key = datastore.NewKey(s.context, ndi.GetType(), "", id, nil)
		_, _ = datastore.Put(s.context, key, ndi)
	} else {
		key := datastore.NewKey(s.context, ndi.GetType(), "", ndi.GetID(), nil)
		_, _ = datastore.Put(s.context, key, ndi)
	}
}

func (s *NodeStorage) Delete(nd Node) error {
	//TODO:name ->deleteNode?
	var ee Node
	q := datastore.NewQuery(nd.GetType()).Filter("ID =", nd.GetID()).KeysOnly()
	//q := datastore.NewQuery(nd.GetType()).Filter("Title =", "korehaURL").KeysOnly()
	keys, err := q.GetAll(s.context, &ee)

	if err != nil {
		return errors.New("Datastore Error")
	}

	if len(keys) < 1 {
		return errors.New("NotFound")
	}

	err = datastore.DeleteMulti(s.context, keys)

	if err != nil {
		return errors.New("Datastore Error:delete")
	}

	return nil
}

// -- low area --
func (s *NodeStorage) UpCast(nodes []interface{}) []Node {
	var node Node
	var nodel []Node

	for _, v:= range(nodes) {
		node = v.(Node)
		nodel = append(nodel, node)
	}

	return nodel
}

func (s *NodeStorage) getNodeURL() []Node {
	q := datastore.NewQuery("NodeURL")

	iter := q.Run(s.context)

	var nodel []Node
	n := new(NodeURL)
	var nt Node
	for{
		n = new(NodeURL)
		_, err := iter.Next(n)
		nt = n

		if err == datastore.Done{
			break
		}

		nodel = append(nodel, nt)
	}

	return nodel
}

func (s *NodeStorage) getNodeText() []Node {
	q := datastore.NewQuery("NodeText")

	iter := q.Run(s.context)

	var nodel []Node
	n := new(NodeText)
	var nt Node
	for{
		n = new(NodeText)
		_, err := iter.Next(n)
		nt = n

		if err == datastore.Done{
			break
		}

		nodel = append(nodel, nt)
	}

	return nodel
}

//func (s *NodeStorage) AllocNode(name string) Node {
//	var nd Node
//
//	if name == "NodeURL" {
//		nd = NewNodeURL()
//	} else if name == "NodeText" {
//		nd = NewNodeText()
//	}
//
//	if nd == nil {
//		return nil
//	}
//
//	return nd
//}
//

//func (s *NodeStorage) GetByID (id int64) Node{
//	key := datastore.NewKey(s.context, "NodeURL", "", id, nil)
//
//	n := s.AllocNode("NodeURL")
//
//	datastore.Get(s.context, key, n)
//
//	return n
//}

/*
func (s *NodeStorage) Flush(){
}

func (s *NodeStorage) GetByKey(key *datastore.Key) {
}

func (s *NodeStorage) GetByKeys(keys *[]datastore.Key) {
}

func (s *NodeManager) UpCast(nodegl interface{}) Node {
	//TODO:Need?
}

func (s *NodeManager) DownCast(nd Node) interface{} {
	//TODO:Need?
	//var i6 int64 = 12321
	//nd.SetId(i6)

	//nd := ndi.(*NodeURL)

	//switch v := ndi.(type) {
	//case NodeURL:
	//	var nd *NodeURL
	//	nd = ndi.(*NodeURL)
	//case NodeText:
	//	var nd *NodeText
	//	nd = ndi.(*NodeText)
	//default:
	//	nd := NewNodeText()
	//}
}
func (s *NodeStorage) 0ldgetNodeURL() []Node {
	q := datastore.NewQuery("NodeURL")

	var ndl []NodeURL
	_, _ = q.GetAll(s.context, &ndl)

	var node Node
	var nodel []Node

	for _, v:= range(ndl) {
		node = v
		nodel = append(nodel, node)
	}

	return nodel
}
*/
<commit_msg>Update<commit_after>package node

import (
	//"fmt"
	//"time"
	//"net/http"
	"errors"
	"context"
	//"google.golang.org/appengine"
	"google.golang.org/appengine/datastore"
	//"strconv"
)

/* == Struct -finder- ============================================== */
type NodeStorage struct{
	context context.Context
	dataTypeList []string
}

type NodeFinder struct{
	Node []Node
}

func (s *NodeFinder) Get() []Node {
	return s.Node
}

func (s *NodeFinder) Title(title string) *NodeFinder {
	var nl []Node

	for _,v := range(s.Node) {
		if v.GetTitle() == title {
			nl = append(nl, v)
		}
	}

	s.Node = nl

	return s
}

func (s *NodeFinder) Description(desc string) *NodeFinder {
	var nl []Node

	for _,v := range(s.Node) {
		if v.GetDescription() == desc {
			nl = append(nl, v)
		}
	}

	s.Node = nl

	return s
}

//Impliment?
//default:strict search
//Add: func (s *NodeFinder) Fuzzy~(title string) *NodeFinder {
//or
//default:fuzzy search
//Add: func (s *NodeFinder) Strict~(title string) *NodeFinder {
//}

//AddFilter?
//Name
//URL?

/* == Struct -Main- ================================================= */
func NewNodeStorage(ctx context.Context) *NodeStorage {
	s := new(NodeStorage)

	s.init(ctx)

	return s
}

func (s *NodeStorage) init(ctx context.Context) {
	s.context = ctx
	s.dataTypeList = []string{
		"NodeURL",
		"NodeText",
	}
}

/* create NodeFinder */
func (s *NodeStorage) Find(dataType string) *NodeFinder {
	nf := new(NodeFinder)

	switch dataType {
	case "NodeURL":
		nf.Node = s.getNodeURL()
	case "NodeText":
		nf.Node = s.getNodeText()
	}

	return nf
}

func (s *NodeStorage) SaveMulti(node []Node) {
	for _, v := range(node) {
		s.Save(v)
	}
}

func (s *NodeStorage) Save(ndi Node) {
	//TODO:name ->newNode/New?
	if ndi.GetID() == 0 {
		key := datastore.NewIncompleteKey(s.context, ndi.GetType(), nil)
		k, _ := datastore.Put(s.context, key, ndi)

		id := k.IntID()
		datastore.Delete(s.context, key)
		datastore.Delete(s.context, k)

		ndi.SetID(id)
		key = datastore.NewKey(s.context, ndi.GetType(), "", id, nil)
		_, _ = datastore.Put(s.context, key, ndi)
	} else {
		key := datastore.NewKey(s.context, ndi.GetType(), "", ndi.GetID(), nil)
		_, _ = datastore.Put(s.context, key, ndi)
	}
}

func (s *NodeStorage) Delete(nd Node) error {
	//TODO:name ->deleteNode?
	var ee Node
	q := datastore.NewQuery(nd.GetType()).Filter("ID =", nd.GetID()).KeysOnly()
	//q := datastore.NewQuery(nd.GetType()).Filter("Title =", "korehaURL").KeysOnly()
	keys, err := q.GetAll(s.context, &ee)

	if err != nil {
		return errors.New("Datastore Error")
	}

	if len(keys) < 1 {
		return errors.New("NotFound")
	}

	err = datastore.DeleteMulti(s.context, keys)

	if err != nil {
		return errors.New("Datastore Error:delete")
	}

	return nil
}

// -- low area --
func (s *NodeStorage) UpCast(nodes []interface{}) []Node {
	var node Node
	var nodel []Node

	for _, v:= range(nodes) {
		node = v.(Node)
		nodel = append(nodel, node)
	}

	return nodel
}

func (s *NodeStorage) getNodeURL() []Node {
	q := datastore.NewQuery("NodeURL")

	iter := q.Run(s.context)

	var nodel []Node
	n := new(NodeURL)
	var nt Node
	for{
		n = new(NodeURL)
		_, err := iter.Next(n)
		nt = n

		if err == datastore.Done{
			break
		}

		nodel = append(nodel, nt)
	}

	return nodel
}

func (s *NodeStorage) getNodeText() []Node {
	q := datastore.NewQuery("NodeText")

	iter := q.Run(s.context)

	var nodel []Node
	n := new(NodeText)
	var nt Node
	for{
		n = new(NodeText)
		_, err := iter.Next(n)
		nt = n

		if err == datastore.Done{
			break
		}

		nodel = append(nodel, nt)
	}

	return nodel
}

//func (s *NodeStorage) AllocNode(name string) Node {
//	var nd Node
//
//	if name == "NodeURL" {
//		nd = NewNodeURL()
//	} else if name == "NodeText" {
//		nd = NewNodeText()
//	}
//
//	if nd == nil {
//		return nil
//	}
//
//	return nd
//}
//

//func (s *NodeStorage) GetByID (id int64) Node{
//	key := datastore.NewKey(s.context, "NodeURL", "", id, nil)
//
//	n := s.AllocNode("NodeURL")
//
//	datastore.Get(s.context, key, n)
//
//	return n
//}

/*
func (s *NodeStorage) Flush(){
}

func (s *NodeStorage) GetByKey(key *datastore.Key) {
}

func (s *NodeStorage) GetByKeys(keys *[]datastore.Key) {
}

func (s *NodeManager) UpCast(nodegl interface{}) Node {
	//TODO:Need?
}

func (s *NodeManager) DownCast(nd Node) interface{} {
	//TODO:Need?
	//var i6 int64 = 12321
	//nd.SetId(i6)

	//nd := ndi.(*NodeURL)

	//switch v := ndi.(type) {
	//case NodeURL:
	//	var nd *NodeURL
	//	nd = ndi.(*NodeURL)
	//case NodeText:
	//	var nd *NodeText
	//	nd = ndi.(*NodeText)
	//default:
	//	nd := NewNodeText()
	//}
}
func (s *NodeStorage) 0ldgetNodeURL() []Node {
	q := datastore.NewQuery("NodeURL")

	var ndl []NodeURL
	_, _ = q.GetAll(s.context, &ndl)

	var node Node
	var nodel []Node

	for _, v:= range(ndl) {
		node = v
		nodel = append(nodel, node)
	}

	return nodel
}
*/
<|endoftext|>


<commit_before>// Copyright 2014 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package gce

import (
	"strings"
	"sync"

	"github.com/juju/errors"

	"github.com/juju/juju/constraints"
	"github.com/juju/juju/environs"
	"github.com/juju/juju/environs/config"
	"github.com/juju/juju/environs/simplestreams"
	"github.com/juju/juju/environs/storage"
	"github.com/juju/juju/instance"
	"github.com/juju/juju/provider/common"
)

type environ struct {
	common.SupportsUnitPlacementPolicy

	name string

	lock    sync.Mutex
	ecfg    *environConfig
	storage storage.Storage

	gce *gceConnection
}

var _ environs.Environ = (*environ)(nil)
var _ simplestreams.HasRegion = (*environ)(nil)

// Name returns the name of the environment.
func (env *environ) Name() string {
	return env.name
}

// Provider returns the environment provider that created this env.
func (*environ) Provider() environs.EnvironProvider {
	return providerInstance
}

// Region returns the CloudSpec to use for the provider, as configured.
func (env *environ) Region() (simplestreams.CloudSpec, error) {
	cloudSpec, err := env.cloudSpec(env.ecfg.region())
	return cloudSpec, errors.Trace(err)
}

func (env *environ) cloudSpec(region string) (simplestreams.CloudSpec, error) {
	cloudSpec := simplestreams.CloudSpec{
		Region:   region,
		Endpoint: env.ecfg.imageURL(),
	}
	return cloudSpec, nil
}

// SetConfig updates the env's configuration.
func (env *environ) SetConfig(cfg *config.Config) error {
	env.lock.Lock()
	defer env.lock.Unlock()
	var oldCfg *config.Config
	if env.ecfg != nil {
		oldCfg = env.ecfg.Config
	}
	ecfg, err := validateConfig(cfg, oldCfg)
	if err != nil {
		return err
	}

	storage, err := newStorage(ecfg)
	if err != nil {
		return err
	}
	env.ecfg = ecfg
	env.storage = storage

	// Connect and authenticate.
	env.gce = ecfg.newConnection()
	err = env.gce.connect(ecfg.auth())

	return errors.Trace(err)
}

func (env *environ) getSnapshot() *environ {
	env.lock.Lock()
	clone := *env
	// TODO(ericsnow) Should env.ecfg be explicitly copied-by-value?
	env.lock.Unlock()

	clone.lock = sync.Mutex{}
	return &clone
}

// Config returns the configuration data with which the env was created.
func (env *environ) Config() *config.Config {
	return env.getSnapshot().ecfg.Config
}

// Storage returns storage specific to the environment.
func (env *environ) Storage() storage.Storage {
	return env.getSnapshot().storage
}

// Bootstrap creates a new instance, chosing the series and arch out of
// available tools. The series and arch are returned along with a func
// that must be called to finalize the bootstrap process by transferring
// the tools and installing the initial juju state server.
func (env *environ) Bootstrap(ctx environs.BootstrapContext, params environs.BootstrapParams) (arch, series string, _ environs.BootstrapFinalizer, _ error) {
	return common.Bootstrap(ctx, env, params)
}

// Destroy shuts down all known machines and destroys the rest of the
// known environment.
func (env *environ) Destroy() error {
	return common.Destroy(env)
}

// instance stuff

var instStatuses = []string{statusPending, statusStaging, statusRunning}

// Instances returns the available instances in the environment that
// match the provided instance IDs.
func (env *environ) Instances(ids []instance.Id) ([]instance.Instance, error) {
	instances, err := env.instances()
	if err != nil {
		return nil, errors.Trace(err)
	}

	// Build the result, matching the provided instance IDs.
	var results []instance.Instance
	for _, id := range ids {
		inst := findInst(id, instances)
		if inst == nil {
			return results, errors.NotFoundf("GCE inst %q", id)
		}
		results = append(results, inst)
	}
	return results, nil
}

func (env *environ) instances() ([]instance.Instance, error) {
	env = env.getSnapshot()

	// instances() only returns instances that are part of the
	// environment (instance IDs matches "juju-<env name>-machine-*").
	// This is important because otherwise juju will see they are not
	// tracked in state, assume they're stale/rogue, and shut them down.
	instances, err := env.gce.instances(env)
	if err != nil {
		return nil, errors.Trace(err)
	}
	// We further filter on the instance status.
	instances = filterInstances(instances, instStatuses...)

	// Turn *compute.Instance values into *environInstance values.
	var results []instance.Instance
	for _, inst := range instances {
		results = append(results, newInstance(inst, env))
	}
	return results, nil
}

// StateServerInstances returns the IDs of the instances corresponding
// to juju state servers.
func (env *environ) StateServerInstances() ([]instance.Id, error) {
	return common.ProviderStateInstances(env, env.Storage())
}

func (env *environ) parsePlacement(placement string) (*gceAvailabilityZone, error) {
	pos := strings.IndexRune(placement, '=')
	if pos == -1 {
		return nil, errors.Errorf("unknown placement directive: %v", placement)
	}
	switch key, value := placement[:pos], placement[pos+1:]; key {
	case "zone":
		zoneName := value
		zones, err := env.AvailabilityZones()
		if err != nil {
			return nil, errors.Trace(err)
		}
		for _, z := range zones {
			if z.Name() == zoneName {
				return z.(*gceAvailabilityZone), nil
			}
		}
		return nil, errors.Errorf("invalid availability zone %q", zoneName)
	}
	return nil, errors.Errorf("unknown placement directive: %v", placement)
}

func checkInstanceType(cons constraints.Value) bool {
	// Constraint has an instance-type constraint so let's see if it is valid.
	for _, itype := range allInstanceTypes {
		if itype.Name == *cons.InstanceType {
			return true
		}
	}
	return false
}
<commit_msg>provider/gce: Address TODO about copying nested pointers by value.<commit_after>// Copyright 2014 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package gce

import (
	"strings"
	"sync"

	"github.com/juju/errors"

	"github.com/juju/juju/constraints"
	"github.com/juju/juju/environs"
	"github.com/juju/juju/environs/config"
	"github.com/juju/juju/environs/simplestreams"
	"github.com/juju/juju/environs/storage"
	"github.com/juju/juju/instance"
	"github.com/juju/juju/provider/common"
)

type environ struct {
	common.SupportsUnitPlacementPolicy

	name string

	lock    sync.Mutex
	ecfg    *environConfig
	storage storage.Storage

	gce *gceConnection
}

var _ environs.Environ = (*environ)(nil)
var _ simplestreams.HasRegion = (*environ)(nil)

// Name returns the name of the environment.
func (env *environ) Name() string {
	return env.name
}

// Provider returns the environment provider that created this env.
func (*environ) Provider() environs.EnvironProvider {
	return providerInstance
}

// Region returns the CloudSpec to use for the provider, as configured.
func (env *environ) Region() (simplestreams.CloudSpec, error) {
	cloudSpec, err := env.cloudSpec(env.ecfg.region())
	return cloudSpec, errors.Trace(err)
}

func (env *environ) cloudSpec(region string) (simplestreams.CloudSpec, error) {
	cloudSpec := simplestreams.CloudSpec{
		Region:   region,
		Endpoint: env.ecfg.imageURL(),
	}
	return cloudSpec, nil
}

// SetConfig updates the env's configuration.
func (env *environ) SetConfig(cfg *config.Config) error {
	env.lock.Lock()
	defer env.lock.Unlock()
	var oldCfg *config.Config
	if env.ecfg != nil {
		oldCfg = env.ecfg.Config
	}
	ecfg, err := validateConfig(cfg, oldCfg)
	if err != nil {
		return err
	}

	storage, err := newStorage(ecfg)
	if err != nil {
		return err
	}
	env.ecfg = ecfg
	env.storage = storage

	// Connect and authenticate.
	env.gce = ecfg.newConnection()
	err = env.gce.connect(ecfg.auth())

	return errors.Trace(err)
}

func (env *environ) getSnapshot() *environ {
	env.lock.Lock()
	clone := *env
	// The config values are all immutable so we don't need to also copy
	// env.ecfg and env.gce by value. If that changes we need to
	// re-evaluate copying them by value.
	env.lock.Unlock()

	clone.lock = sync.Mutex{}
	return &clone
}

// Config returns the configuration data with which the env was created.
func (env *environ) Config() *config.Config {
	return env.getSnapshot().ecfg.Config
}

// Storage returns storage specific to the environment.
func (env *environ) Storage() storage.Storage {
	return env.getSnapshot().storage
}

// Bootstrap creates a new instance, chosing the series and arch out of
// available tools. The series and arch are returned along with a func
// that must be called to finalize the bootstrap process by transferring
// the tools and installing the initial juju state server.
func (env *environ) Bootstrap(ctx environs.BootstrapContext, params environs.BootstrapParams) (arch, series string, _ environs.BootstrapFinalizer, _ error) {
	return common.Bootstrap(ctx, env, params)
}

// Destroy shuts down all known machines and destroys the rest of the
// known environment.
func (env *environ) Destroy() error {
	return common.Destroy(env)
}

// instance stuff

var instStatuses = []string{statusPending, statusStaging, statusRunning}

// Instances returns the available instances in the environment that
// match the provided instance IDs.
func (env *environ) Instances(ids []instance.Id) ([]instance.Instance, error) {
	instances, err := env.instances()
	if err != nil {
		return nil, errors.Trace(err)
	}

	// Build the result, matching the provided instance IDs.
	var results []instance.Instance
	for _, id := range ids {
		inst := findInst(id, instances)
		if inst == nil {
			return results, errors.NotFoundf("GCE inst %q", id)
		}
		results = append(results, inst)
	}
	return results, nil
}

func (env *environ) instances() ([]instance.Instance, error) {
	env = env.getSnapshot()

	// instances() only returns instances that are part of the
	// environment (instance IDs matches "juju-<env name>-machine-*").
	// This is important because otherwise juju will see they are not
	// tracked in state, assume they're stale/rogue, and shut them down.
	instances, err := env.gce.instances(env)
	if err != nil {
		return nil, errors.Trace(err)
	}
	// We further filter on the instance status.
	instances = filterInstances(instances, instStatuses...)

	// Turn *compute.Instance values into *environInstance values.
	var results []instance.Instance
	for _, inst := range instances {
		results = append(results, newInstance(inst, env))
	}
	return results, nil
}

// StateServerInstances returns the IDs of the instances corresponding
// to juju state servers.
func (env *environ) StateServerInstances() ([]instance.Id, error) {
	return common.ProviderStateInstances(env, env.Storage())
}

func (env *environ) parsePlacement(placement string) (*gceAvailabilityZone, error) {
	pos := strings.IndexRune(placement, '=')
	if pos == -1 {
		return nil, errors.Errorf("unknown placement directive: %v", placement)
	}
	switch key, value := placement[:pos], placement[pos+1:]; key {
	case "zone":
		zoneName := value
		zones, err := env.AvailabilityZones()
		if err != nil {
			return nil, errors.Trace(err)
		}
		for _, z := range zones {
			if z.Name() == zoneName {
				return z.(*gceAvailabilityZone), nil
			}
		}
		return nil, errors.Errorf("invalid availability zone %q", zoneName)
	}
	return nil, errors.Errorf("unknown placement directive: %v", placement)
}

func checkInstanceType(cons constraints.Value) bool {
	// Constraint has an instance-type constraint so let's see if it is valid.
	for _, itype := range allInstanceTypes {
		if itype.Name == *cons.InstanceType {
			return true
		}
	}
	return false
}
<|endoftext|>


<commit_before>package etd

import (
	"encoding/json"

	"cloud.google.com/go/pubsub"
	"github.com/googlecloudplatform/threat-automation/entities"
	"github.com/pkg/errors"
)

type badIP struct {
	InsertID    string `json:"insertId"`
	LogName     string `json:"logName"`
	JSONPayload struct {
		AffectedResources []struct {
			GCPResourceName string
		}
		DetectionCategory struct {
			RuleName string
		}
		Properties struct {
			Location       string
			ProjectID      string `json:"project_id"`
			SourceInstance string
		}
	}
}

// BadIP is an ETD anomalous IAM grant subrule from StackDriver.
type BadIP struct {
	// Fields found in every ETD finding not specific to this finding.
	*Finding
	// Fields specific to this finding.
	fields badIP
}

// NewBadIP reads a pubsub message and creates a new finding.
func NewBadIP(ps *pubsub.Message) (*BadIP, error) {
	var f BadIP
	if err := json.Unmarshal(ps.Data, &f.fields); err != nil {
		return nil, errors.Wrap(entities.ErrUnmarshal, err.Error())
	}
	b, _ := NewFinding(ps)
	f.Finding = b
	if v := f.validate(); !v {
		return nil, errors.Wrap(entities.ErrValueNotFound, "fields did not validate")
	}
	return &f, nil
}

// validate ensures the fields to be accessed by getter methods are valid.
func (f *BadIP) validate() bool {
	// TODO: Implement this.
	return true
}

// Zone returns the zone of affected project.
func (f *BadIP) Zone() string {
	return f.fields.JSONPayload.Properties.Location
}

// Instance returns the zone of affected project.
func (f *BadIP) Instance() string {
	s := f.fields.JSONPayload.Properties.SourceInstance
	i := extractInstance.FindStringSubmatch(s)
	if len(i) != 2 {
		return ""
	}
	return i[1]
}
<commit_msg>remove projectid from struct<commit_after>package etd

import (
	"encoding/json"

	"cloud.google.com/go/pubsub"
	"github.com/googlecloudplatform/threat-automation/entities"
	"github.com/pkg/errors"
)

type badIP struct {
	InsertID    string `json:"insertId"`
	LogName     string `json:"logName"`
	JSONPayload struct {
		AffectedResources []struct {
			GCPResourceName string
		}
		DetectionCategory struct {
			RuleName string
		}
		Properties struct {
			Location       string
			SourceInstance string
		}
	}
}

// BadIP is an ETD anomalous IAM grant subrule from StackDriver.
type BadIP struct {
	// Fields found in every ETD finding not specific to this finding.
	*Finding
	// Fields specific to this finding.
	fields badIP
}

// NewBadIP reads a pubsub message and creates a new finding.
func NewBadIP(ps *pubsub.Message) (*BadIP, error) {
	var f BadIP
	if err := json.Unmarshal(ps.Data, &f.fields); err != nil {
		return nil, errors.Wrap(entities.ErrUnmarshal, err.Error())
	}
	b, _ := NewFinding(ps)
	f.Finding = b
	if v := f.validate(); !v {
		return nil, errors.Wrap(entities.ErrValueNotFound, "fields did not validate")
	}
	return &f, nil
}

// validate ensures the fields to be accessed by getter methods are valid.
func (f *BadIP) validate() bool {
	// TODO: Implement this.
	return true
}

// Zone returns the zone of affected project.
func (f *BadIP) Zone() string {
	return f.fields.JSONPayload.Properties.Location
}

// Instance returns the zone of affected project.
func (f *BadIP) Instance() string {
	s := f.fields.JSONPayload.Properties.SourceInstance
	i := extractInstance.FindStringSubmatch(s)
	if len(i) != 2 {
		return ""
	}
	return i[1]
}
<|endoftext|>