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

// Package ebnf is a library for EBNF grammars. The input is text ([]byte)
// satisfying the following grammar (represented itself in EBNF):
//
//	Production  = name "=" [ Expression ] "." .
//	Expression  = Alternative { "|" Alternative } .
//	Alternative = Term { Term } .
//	Term        = name | token [ "..." token ] | Group | Option | Repetition .
//	Group       = "(" Expression ")" .
//	Option      = "[" Expression "]" .
//	Repetition  = "{" Expression "}" .
//
// A name is a Go identifier, a token is a Go string, and comments
// and white space follow the same rules as for the Go language.
// Production names starting with an uppercase Unicode letter denote
// non-terminal productions (i.e., productions which allow white-space
// and comments between tokens); all other production names denote
// lexical productions.
//
package ebnf

import (
	"go/scanner"
	"go/token"
	"os"
	"unicode"
	"utf8"
)

// ----------------------------------------------------------------------------
// Internal representation

type (
	// An Expression node represents a production expression.
	Expression interface {
		// Pos is the position of the first character of the syntactic construct
		Pos() token.Pos
	}

	// An Alternative node represents a non-empty list of alternative expressions.
	Alternative []Expression // x | y | z

	// A Sequence node represents a non-empty list of sequential expressions.
	Sequence []Expression // x y z

	// A Name node represents a production name.
	Name struct {
		StringPos token.Pos
		String    string
	}

	// A Token node represents a literal.
	Token struct {
		StringPos token.Pos
		String    string
	}

	// A List node represents a range of characters.
	Range struct {
		Begin, End *Token // begin ... end
	}

	// A Group node represents a grouped expression.
	Group struct {
		Lparen token.Pos
		Body   Expression // (body)
	}

	// An Option node represents an optional expression.
	Option struct {
		Lbrack token.Pos
		Body   Expression // [body]
	}

	// A Repetition node represents a repeated expression.
	Repetition struct {
		Lbrace token.Pos
		Body   Expression // {body}
	}

	// A Production node represents an EBNF production.
	Production struct {
		Name *Name
		Expr Expression
	}

	// A Grammar is a set of EBNF productions. The map
	// is indexed by production name.
	//
	Grammar map[string]*Production
)

func (x Alternative) Pos() token.Pos { return x[0].Pos() } // the parser always generates non-empty Alternative
func (x Sequence) Pos() token.Pos    { return x[0].Pos() } // the parser always generates non-empty Sequences
func (x *Name) Pos() token.Pos       { return x.StringPos }
func (x *Token) Pos() token.Pos      { return x.StringPos }
func (x *Range) Pos() token.Pos      { return x.Begin.Pos() }
func (x *Group) Pos() token.Pos      { return x.Lparen }
func (x *Option) Pos() token.Pos     { return x.Lbrack }
func (x *Repetition) Pos() token.Pos { return x.Lbrace }
func (x *Production) Pos() token.Pos { return x.Name.Pos() }

// ----------------------------------------------------------------------------
// Grammar verification

func isLexical(name string) bool {
	ch, _ := utf8.DecodeRuneInString(name)
	return !unicode.IsUpper(ch)
}

type verifier struct {
	fset *token.FileSet
	scanner.ErrorVector
	worklist []*Production
	reached  Grammar // set of productions reached from (and including) the root production
	grammar  Grammar
}

func (v *verifier) error(pos token.Pos, msg string) {
	v.Error(v.fset.Position(pos), msg)
}

func (v *verifier) push(prod *Production) {
	name := prod.Name.String
	if _, found := v.reached[name]; !found {
		v.worklist = append(v.worklist, prod)
		v.reached[name] = prod
	}
}

func (v *verifier) verifyChar(x *Token) int {
	s := x.String
	if utf8.RuneCountInString(s) != 1 {
		v.error(x.Pos(), "single char expected, found "+s)
		return 0
	}
	ch, _ := utf8.DecodeRuneInString(s)
	return ch
}

func (v *verifier) verifyExpr(expr Expression, lexical bool) {
	switch x := expr.(type) {
	case nil:
		// empty expression
	case Alternative:
		for _, e := range x {
			v.verifyExpr(e, lexical)
		}
	case Sequence:
		for _, e := range x {
			v.verifyExpr(e, lexical)
		}
	case *Name:
		// a production with this name must exist;
		// add it to the worklist if not yet processed
		if prod, found := v.grammar[x.String]; found {
			v.push(prod)
		} else {
			v.error(x.Pos(), "missing production "+x.String)
		}
		// within a lexical production references
		// to non-lexical productions are invalid
		if lexical && !isLexical(x.String) {
			v.error(x.Pos(), "reference to non-lexical production "+x.String)
		}
	case *Token:
		// nothing to do for now
	case *Range:
		i := v.verifyChar(x.Begin)
		j := v.verifyChar(x.End)
		if i >= j {
			v.error(x.Pos(), "decreasing character range")
		}
	case *Group:
		v.verifyExpr(x.Body, lexical)
	case *Option:
		v.verifyExpr(x.Body, lexical)
	case *Repetition:
		v.verifyExpr(x.Body, lexical)
	default:
		panic("unreachable")
	}
}

func (v *verifier) verify(fset *token.FileSet, grammar Grammar, start string) {
	// find root production
	root, found := grammar[start]
	if !found {
		// token.NoPos doesn't require a file set;
		// ok to set v.fset only afterwards
		v.error(token.NoPos, "no start production "+start)
		return
	}

	// initialize verifier
	v.fset = fset
	v.ErrorVector.Reset()
	v.worklist = v.worklist[0:0]
	v.reached = make(Grammar)
	v.grammar = grammar

	// work through the worklist
	v.push(root)
	for {
		n := len(v.worklist) - 1
		if n < 0 {
			break
		}
		prod := v.worklist[n]
		v.worklist = v.worklist[0:n]
		v.verifyExpr(prod.Expr, isLexical(prod.Name.String))
	}

	// check if all productions were reached
	if len(v.reached) < len(v.grammar) {
		for name, prod := range v.grammar {
			if _, found := v.reached[name]; !found {
				v.error(prod.Pos(), name+" is unreachable")
			}
		}
	}
}

// Verify checks that:
//	- all productions used are defined
//	- all productions defined are used when beginning at start
//	- lexical productions refer only to other lexical productions
//
// Position information is interpreted relative to the file set fset.
//
func Verify(fset *token.FileSet, grammar Grammar, start string) os.Error {
	var v verifier
	v.verify(fset, grammar, start)
	return v.GetError(scanner.Sorted)
}
<commit_msg>ebnf: update comment<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package ebnf is a library for EBNF grammars. The input is text ([]byte)
// satisfying the following grammar (represented itself in EBNF):
//
//	Production  = name "=" [ Expression ] "." .
//	Expression  = Alternative { "|" Alternative } .
//	Alternative = Term { Term } .
//	Term        = name | token [ "…" token ] | Group | Option | Repetition .
//	Group       = "(" Expression ")" .
//	Option      = "[" Expression "]" .
//	Repetition  = "{" Expression "}" .
//
// A name is a Go identifier, a token is a Go string, and comments
// and white space follow the same rules as for the Go language.
// Production names starting with an uppercase Unicode letter denote
// non-terminal productions (i.e., productions which allow white-space
// and comments between tokens); all other production names denote
// lexical productions.
//
package ebnf

import (
	"go/scanner"
	"go/token"
	"os"
	"unicode"
	"utf8"
)

// ----------------------------------------------------------------------------
// Internal representation

type (
	// An Expression node represents a production expression.
	Expression interface {
		// Pos is the position of the first character of the syntactic construct
		Pos() token.Pos
	}

	// An Alternative node represents a non-empty list of alternative expressions.
	Alternative []Expression // x | y | z

	// A Sequence node represents a non-empty list of sequential expressions.
	Sequence []Expression // x y z

	// A Name node represents a production name.
	Name struct {
		StringPos token.Pos
		String    string
	}

	// A Token node represents a literal.
	Token struct {
		StringPos token.Pos
		String    string
	}

	// A List node represents a range of characters.
	Range struct {
		Begin, End *Token // begin ... end
	}

	// A Group node represents a grouped expression.
	Group struct {
		Lparen token.Pos
		Body   Expression // (body)
	}

	// An Option node represents an optional expression.
	Option struct {
		Lbrack token.Pos
		Body   Expression // [body]
	}

	// A Repetition node represents a repeated expression.
	Repetition struct {
		Lbrace token.Pos
		Body   Expression // {body}
	}

	// A Production node represents an EBNF production.
	Production struct {
		Name *Name
		Expr Expression
	}

	// A Grammar is a set of EBNF productions. The map
	// is indexed by production name.
	//
	Grammar map[string]*Production
)

func (x Alternative) Pos() token.Pos { return x[0].Pos() } // the parser always generates non-empty Alternative
func (x Sequence) Pos() token.Pos    { return x[0].Pos() } // the parser always generates non-empty Sequences
func (x *Name) Pos() token.Pos       { return x.StringPos }
func (x *Token) Pos() token.Pos      { return x.StringPos }
func (x *Range) Pos() token.Pos      { return x.Begin.Pos() }
func (x *Group) Pos() token.Pos      { return x.Lparen }
func (x *Option) Pos() token.Pos     { return x.Lbrack }
func (x *Repetition) Pos() token.Pos { return x.Lbrace }
func (x *Production) Pos() token.Pos { return x.Name.Pos() }

// ----------------------------------------------------------------------------
// Grammar verification

func isLexical(name string) bool {
	ch, _ := utf8.DecodeRuneInString(name)
	return !unicode.IsUpper(ch)
}

type verifier struct {
	fset *token.FileSet
	scanner.ErrorVector
	worklist []*Production
	reached  Grammar // set of productions reached from (and including) the root production
	grammar  Grammar
}

func (v *verifier) error(pos token.Pos, msg string) {
	v.Error(v.fset.Position(pos), msg)
}

func (v *verifier) push(prod *Production) {
	name := prod.Name.String
	if _, found := v.reached[name]; !found {
		v.worklist = append(v.worklist, prod)
		v.reached[name] = prod
	}
}

func (v *verifier) verifyChar(x *Token) int {
	s := x.String
	if utf8.RuneCountInString(s) != 1 {
		v.error(x.Pos(), "single char expected, found "+s)
		return 0
	}
	ch, _ := utf8.DecodeRuneInString(s)
	return ch
}

func (v *verifier) verifyExpr(expr Expression, lexical bool) {
	switch x := expr.(type) {
	case nil:
		// empty expression
	case Alternative:
		for _, e := range x {
			v.verifyExpr(e, lexical)
		}
	case Sequence:
		for _, e := range x {
			v.verifyExpr(e, lexical)
		}
	case *Name:
		// a production with this name must exist;
		// add it to the worklist if not yet processed
		if prod, found := v.grammar[x.String]; found {
			v.push(prod)
		} else {
			v.error(x.Pos(), "missing production "+x.String)
		}
		// within a lexical production references
		// to non-lexical productions are invalid
		if lexical && !isLexical(x.String) {
			v.error(x.Pos(), "reference to non-lexical production "+x.String)
		}
	case *Token:
		// nothing to do for now
	case *Range:
		i := v.verifyChar(x.Begin)
		j := v.verifyChar(x.End)
		if i >= j {
			v.error(x.Pos(), "decreasing character range")
		}
	case *Group:
		v.verifyExpr(x.Body, lexical)
	case *Option:
		v.verifyExpr(x.Body, lexical)
	case *Repetition:
		v.verifyExpr(x.Body, lexical)
	default:
		panic("unreachable")
	}
}

func (v *verifier) verify(fset *token.FileSet, grammar Grammar, start string) {
	// find root production
	root, found := grammar[start]
	if !found {
		// token.NoPos doesn't require a file set;
		// ok to set v.fset only afterwards
		v.error(token.NoPos, "no start production "+start)
		return
	}

	// initialize verifier
	v.fset = fset
	v.ErrorVector.Reset()
	v.worklist = v.worklist[0:0]
	v.reached = make(Grammar)
	v.grammar = grammar

	// work through the worklist
	v.push(root)
	for {
		n := len(v.worklist) - 1
		if n < 0 {
			break
		}
		prod := v.worklist[n]
		v.worklist = v.worklist[0:n]
		v.verifyExpr(prod.Expr, isLexical(prod.Name.String))
	}

	// check if all productions were reached
	if len(v.reached) < len(v.grammar) {
		for name, prod := range v.grammar {
			if _, found := v.reached[name]; !found {
				v.error(prod.Pos(), name+" is unreachable")
			}
		}
	}
}

// Verify checks that:
//	- all productions used are defined
//	- all productions defined are used when beginning at start
//	- lexical productions refer only to other lexical productions
//
// Position information is interpreted relative to the file set fset.
//
func Verify(fset *token.FileSet, grammar Grammar, start string) os.Error {
	var v verifier
	v.verify(fset, grammar, start)
	return v.GetError(scanner.Sorted)
}
<|endoftext|>


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

package sync

import (
	"sync/atomic"
)

// Once is an object that will perform exactly one action.
type Once struct {
	m    Mutex
	done int32
}

// Do calls the function f if and only if the method is being called for the
// first time with this receiver.  In other words, given
// 	var once Once
// if once.Do(f) is called multiple times, only the first call will invoke f,
// even if f has a different value in each invocation.  A new instance of
// Once is required for each function to execute.
//
// Do is intended for initialization that must be run exactly once.  Since f
// is niladic, it may be necessary to use a function literal to capture the
// arguments to a function to be invoked by Do:
// 	config.once.Do(func() { config.init(filename) })
//
// Because no call to Do returns until the one call to f returns, if f causes
// Do to be called, it will deadlock.
//
func (o *Once) Do(f func()) {
	if atomic.AddInt32(&o.done, 0) == 1 {
		return
	}
	// Slow-path.
	o.m.Lock()
	defer o.m.Unlock()
	if o.done == 0 {
		f()
		atomic.CompareAndSwapInt32(&o.done, 0, 1)
	}
}
<commit_msg>sync: improve Once fast path Use atomic.LoadUint32(&done) instead of atomic.AddInt32(&done, 0) on fast path.<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package sync

import (
	"sync/atomic"
)

// Once is an object that will perform exactly one action.
type Once struct {
	m    Mutex
	done uint32
}

// Do calls the function f if and only if the method is being called for the
// first time with this receiver.  In other words, given
// 	var once Once
// if once.Do(f) is called multiple times, only the first call will invoke f,
// even if f has a different value in each invocation.  A new instance of
// Once is required for each function to execute.
//
// Do is intended for initialization that must be run exactly once.  Since f
// is niladic, it may be necessary to use a function literal to capture the
// arguments to a function to be invoked by Do:
// 	config.once.Do(func() { config.init(filename) })
//
// Because no call to Do returns until the one call to f returns, if f causes
// Do to be called, it will deadlock.
//
func (o *Once) Do(f func()) {
	if atomic.LoadUint32(&o.done) == 1 {
		return
	}
	// Slow-path.
	o.m.Lock()
	defer o.m.Unlock()
	if o.done == 0 {
		f()
		atomic.CompareAndSwapUint32(&o.done, 0, 1)
	}
}
<|endoftext|>


<commit_before>package goquery

import (
	"testing"

	"code.google.com/p/go.net/html"
)

func TestEach(t *testing.T) {
	var cnt int

	sel := Doc().Find(".hero-unit .row-fluid").Each(func(i int, n *Selection) {
		cnt++
		t.Logf("At index %v, node %v", i, n.Nodes[0].Data)
	}).Find("a")

	if cnt != 4 {
		t.Errorf("Expected Each() to call function 4 times, got %v times.", cnt)
	}
	AssertLength(t, sel.Nodes, 6)
}

func TestEachWithBreak(t *testing.T) {
	var cnt int

	sel := Doc().Find(".hero-unit .row-fluid").EachWithBreak(func(i int, n *Selection) bool {
		cnt++
		t.Logf("At index %v, node %v", i, n.Nodes[0].Data)
		return false
	}).Find("a")

	if cnt != 1 {
		t.Errorf("Expected Each() to call function 1 time, got %v times.", cnt)
	}
	AssertLength(t, sel.Nodes, 6)
}

func TestEachEmptySelection(t *testing.T) {
	var cnt int

	sel := Doc().Find("zzzz")
	sel.Each(func(i int, n *Selection) {
		cnt++
	})
	if cnt > 0 {
		t.Error("Expected Each() to not be called on empty Selection.")
	}
	sel2 := sel.Find("div")
	AssertLength(t, sel2.Nodes, 0)
}

func TestMap(t *testing.T) {
	sel := Doc().Find(".pvk-content")
	vals := sel.Map(func(i int, s *Selection) string {
		n := s.Get(0)
		if n.Type == html.ElementNode {
			return n.Data
		}
		return ""
	})
	for _, v := range vals {
		if v != "div" {
			t.Error("Expected Map array result to be all 'div's.")
		}
	}
	if len(vals) != 3 {
		t.Errorf("Expected Map array result to have a length of 3, found %v.", len(vals))
	}
}

func TestForRange(t *testing.T) {
	sel := Doc().Find(".pvk-content")
	initLen := sel.Length()
	for i, l := 0, sel.Length(); i < l; i++ {
		single := sel.Eq(i)
		//h, err := single.Html()
		//if err != nil {
		//	t.Fatal(err)
		//}
		//fmt.Println(i, h)
		if single.Length() != 1 {
			t.Errorf("%d: expected length of 1, got %d", i, single.Length())
		}
	}
	if sel.Length() != initLen {
		t.Errorf("expected initial selection to still have length %d, got %d", initLen, sel.Length())
	}
}
<commit_msg>simpler for-range construct to loop over selections<commit_after>package goquery

import (
	"testing"

	"code.google.com/p/go.net/html"
)

func TestEach(t *testing.T) {
	var cnt int

	sel := Doc().Find(".hero-unit .row-fluid").Each(func(i int, n *Selection) {
		cnt++
		t.Logf("At index %v, node %v", i, n.Nodes[0].Data)
	}).Find("a")

	if cnt != 4 {
		t.Errorf("Expected Each() to call function 4 times, got %v times.", cnt)
	}
	AssertLength(t, sel.Nodes, 6)
}

func TestEachWithBreak(t *testing.T) {
	var cnt int

	sel := Doc().Find(".hero-unit .row-fluid").EachWithBreak(func(i int, n *Selection) bool {
		cnt++
		t.Logf("At index %v, node %v", i, n.Nodes[0].Data)
		return false
	}).Find("a")

	if cnt != 1 {
		t.Errorf("Expected Each() to call function 1 time, got %v times.", cnt)
	}
	AssertLength(t, sel.Nodes, 6)
}

func TestEachEmptySelection(t *testing.T) {
	var cnt int

	sel := Doc().Find("zzzz")
	sel.Each(func(i int, n *Selection) {
		cnt++
	})
	if cnt > 0 {
		t.Error("Expected Each() to not be called on empty Selection.")
	}
	sel2 := sel.Find("div")
	AssertLength(t, sel2.Nodes, 0)
}

func TestMap(t *testing.T) {
	sel := Doc().Find(".pvk-content")
	vals := sel.Map(func(i int, s *Selection) string {
		n := s.Get(0)
		if n.Type == html.ElementNode {
			return n.Data
		}
		return ""
	})
	for _, v := range vals {
		if v != "div" {
			t.Error("Expected Map array result to be all 'div's.")
		}
	}
	if len(vals) != 3 {
		t.Errorf("Expected Map array result to have a length of 3, found %v.", len(vals))
	}
}

func TestForRange(t *testing.T) {
	sel := Doc().Find(".pvk-content")
	initLen := sel.Length()
	for i := range sel.Nodes {
		single := sel.Eq(i)
		//h, err := single.Html()
		//if err != nil {
		//	t.Fatal(err)
		//}
		//fmt.Println(i, h)
		if single.Length() != 1 {
			t.Errorf("%d: expected length of 1, got %d", i, single.Length())
		}
	}
	if sel.Length() != initLen {
		t.Errorf("expected initial selection to still have length %d, got %d", initLen, sel.Length())
	}
}
<|endoftext|>


<commit_before>package server

import (
	"html/template"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strings"

	"github.com/gin-gonic/gin/render"
	"github.com/k0kubun/pp"
)

type Render struct {
	Templates          map[string]*template.Template
	AssetsDir          string
	TemplatesDir       string
	AssetsTemplatesDir string
	Layout             string
	Ext                string
	TemplateFuncMap    map[string]interface{}
	Debug              bool
	UseFS              bool
}

func NewRender() Render {
	pwd, err := os.Getwd()
	if err != nil {
		log.Fatalln(err)
	}

	r := Render{

		Templates: map[string]*template.Template{},

		AssetsDir: pwd,
		// TemplatesDir holds the location of the templates
		TemplatesDir: "assets/views/",
		// Layout is the file name of the layout file
		Layout: "layout.html",
		// Ext is the file extension of the rendered templates
		Ext: ".html",
		// Template's function map
		TemplateFuncMap: nil,
		// Debug enables debug mode
		Debug: false,

		UseFS: false,
	}

	return r
}

func (r Render) Glob(pattern string) ([]string, error) {
	var matches []string
	if r.UseFS == true {
		return filepath.Glob(pattern)
	} else {
		for _, key := range AssetNames() {
			if ok, err := filepath.Match(pattern, key); ok && err == nil {
				matches = append(matches, key)
			}
		}
		return matches, nil
	}
}

func (r Render) Init() Render {
	if _, err := os.Stat(r.TemplatesDir); !os.IsNotExist(err) {
		// assets exists on disk, UseFS
		r.UseFS = true
	} else {
		// assets from bindData
		r.UseFS = false
	}

	layout := r.TemplatesDir + r.Layout
	viewDirs, _ := r.Glob(r.TemplatesDir + "**" + string(os.PathSeparator) + "*" + r.Ext)
	partials, _ := r.Glob(r.TemplatesDir + "_*" + r.Ext)

	for _, view := range viewDirs {
		renderName := r.getRenderName(view)
		if r.Debug {
			log.Printf("[GIN-debug] %-6s %-25s --> %s\n", "LOAD", view, renderName)
		}
		views := append(partials, layout, view)
		r.AddFromFiles(renderName, views...)
	}

	return r
}

func (r Render) getRenderName(tpl string) string {
	tmp := strings.TrimPrefix(tpl, r.TemplatesDir)
	tmp = strings.TrimSuffix(tmp, r.Ext)
	tmp = strings.Replace(tmp, "\"", "/", -1)
	return tmp
}

func (r Render) Add(name string, tmpl *template.Template) {
	if tmpl == nil {
		panic("template can not be nil")
	}
	if len(name) == 0 {
		panic("template name cannot be empty")
	}
	r.Templates[name] = tmpl
}

func (r Render) AddFromFiles(name string, files ...string) *template.Template {
	tmpl := template.New(filepath.Base(r.Layout)).Funcs(r.TemplateFuncMap)
	for _, f := range files {
		templateString, err := ioutil.ReadFile(f)
		if err != nil {
			if r.Debug {
				pp.Printf("[GIN-debug] reading from BindData --> %s (%s)\n", f, err.Error())
			}
			templateString, err = Asset(f)
		} else {
			if r.Debug {
				pp.Printf("[GIN-debug] reading from FS --> %s\n", f)
			}
		}

		if err != nil {
			log.Fatal(err)
		}

		tmpl, err = tmpl.Parse(string(templateString))
		if err != nil {
			log.Fatal(err)
		}
	}
	r.Add(name, tmpl)
	return tmpl
}

func (r Render) Instance(name string, data interface{}) render.Render {
	if r.Debug == true {
		r.Init()
		pp.Println("data -->", data)
	}

	return render.HTML{
		Template: r.Templates[name],
		Data:     data,
	}
}
<commit_msg>ui : fix asset paths on windows<commit_after>package server

import (
	"html/template"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strings"

	"github.com/gin-gonic/gin/render"
	"github.com/k0kubun/pp"
)

type Render struct {
	Templates          map[string]*template.Template
	AssetsDir          string
	TemplatesDir       string
	AssetsTemplatesDir string
	Layout             string
	Ext                string
	TemplateFuncMap    map[string]interface{}
	Debug              bool
	UseFS              bool
}

func NewRender() Render {
	pwd, err := os.Getwd()
	if err != nil {
		log.Fatalln(err)
	}

	r := Render{

		Templates: map[string]*template.Template{},

		AssetsDir: pwd,
		// TemplatesDir holds the location of the templates
		TemplatesDir: "assets/views/",
		// Layout is the file name of the layout file
		Layout: "layout.html",
		// Ext is the file extension of the rendered templates
		Ext: ".html",
		// Template's function map
		TemplateFuncMap: nil,
		// Debug enables debug mode
		Debug: false,

		UseFS: false,
	}

	return r
}

func (r Render) Glob(pattern string) ([]string, error) {
	var matches []string
	if r.UseFS == true {
		return filepath.Glob(pattern)
	} else {
		for _, key := range AssetNames() {
			if ok, err := filepath.Match(pattern, key); ok && err == nil {
				matches = append(matches, key)
			}
		}
		return matches, nil
	}
}

func (r Render) Init() Render {
	pathSeparator := string(os.PathSeparator)
	if _, err := os.Stat(r.TemplatesDir); !os.IsNotExist(err) {
		// assets exists on disk, UseFS
		r.UseFS = true
	} else {
		// assets from bindData
		r.UseFS = false
		pathSeparator = "/"
	}

	layout := r.TemplatesDir + r.Layout
	viewDirs, _ := r.Glob(r.TemplatesDir + "**" + pathSeparator + "*" + r.Ext)
	partials, _ := r.Glob(r.TemplatesDir + "_*" + r.Ext)

	for _, view := range viewDirs {
		renderName := r.getRenderName(view)
		if r.Debug {
			log.Printf("[GIN-debug] %-6s %-25s --> %s\n", "LOAD", view, renderName)
		}
		views := append(partials, layout, view)
		r.AddFromFiles(renderName, views...)
	}

	return r
}

func (r Render) getRenderName(tpl string) string {
	tmp := strings.TrimPrefix(tpl, r.TemplatesDir)
	tmp = strings.TrimSuffix(tmp, r.Ext)
	tmp = strings.Replace(tmp, "\"", "/", -1)
	return tmp
}

func (r Render) Add(name string, tmpl *template.Template) {
	if tmpl == nil {
		panic("template can not be nil")
	}
	if len(name) == 0 {
		panic("template name cannot be empty")
	}
	r.Templates[name] = tmpl
}

func (r Render) AddFromFiles(name string, files ...string) *template.Template {
	tmpl := template.New(filepath.Base(r.Layout)).Funcs(r.TemplateFuncMap)
	for _, f := range files {
		templateString, err := ioutil.ReadFile(f)
		if err != nil {
			if r.Debug {
				pp.Printf("[GIN-debug] reading from BindData --> %s (%s)\n", f, err.Error())
			}
			templateString, err = Asset(f)
		} else {
			if r.Debug {
				pp.Printf("[GIN-debug] reading from FS --> %s\n", f)
			}
		}

		if err != nil {
			log.Fatal(err)
		}

		tmpl, err = tmpl.Parse(string(templateString))
		if err != nil {
			log.Fatal(err)
		}
	}
	r.Add(name, tmpl)
	return tmpl
}

func (r Render) Instance(name string, data interface{}) render.Render {
	if r.Debug == true {
		r.Init()
		pp.Println("data -->", data)
	}

	return render.HTML{
		Template: r.Templates[name],
		Data:     data,
	}
}
<|endoftext|>


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

package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"os"

	"github.com/gogo/protobuf/proto"
	log "github.com/golang/glog"
	"github.com/mesos/mesos-go/auth"
	"github.com/mesos/mesos-go/auth/sasl"
	"github.com/mesos/mesos-go/auth/sasl/mech"
	mesos "github.com/mesos/mesos-go/mesosproto"
	"github.com/mesos/mesos-go/scheduler"
	"github.com/samuel/go-zookeeper/zk"
	"golang.org/x/net/context"

	"github.com/mesosphere/etcd-mesos/rpc"
	etcdscheduler "github.com/mesosphere/etcd-mesos/scheduler"
)

func parseIP(address string) net.IP {
	addr, err := net.LookupIP(address)
	if err != nil {
		log.Fatal(err)
	}
	if len(addr) < 1 {
		log.Fatalf("failed to parse IP from address '%v'", address)
	}
	return addr[0]
}

func main() {
	clusterName :=
		flag.String("cluster-name", "default", "Unique name of this etcd cluster")
	master :=
		flag.String("master", "127.0.0.1:5050", "Master address <ip:port>")
	zkFrameworkPersist :=
		flag.String("zk-framework-persist", "", "Zookeeper URI of the form zk://host1:port1,host2:port2/chroot/path")
	taskCount :=
		flag.Int("cluster-size", 5, "Total task count to run")
	adminPort :=
		flag.Int("admin-port", 23400, "Binding port for admin interface")
	reseedTimeout :=
		flag.Int("reseed-timeout", 240, "Seconds of etcd livelock to wait for before attempting a cluster re-seed")
	autoReseed :=
		flag.Bool("auto-reseed", true, "Perform automatic cluster reseed when the "+
			"cluster has been livelocked for -reseed-timeout seconds")
	artifactPort :=
		flag.Int("artifact-port", 12300, "Binding port for artifact server")
	sandboxDisk :=
		flag.Float64("sandbox-disk-limit", 4096, "Max disk usage for the etcd mesos sandbox in MB")
	sandboxCpu :=
		flag.Float64("sandbox-cpu-limit", 4, "Max cpu usage for the etcd mesos sandbox in MB")
	sandboxMem :=
		flag.Float64("sandbox-mem-limit", 2048, "Max memory usage for the etcd mesos sandbox in MB")
	executorPath :=
		flag.String("executor-bin", "./bin/etcd-mesos-executor", "Path to executor binary")
	etcdPath :=
		flag.String("etcd-bin", "./bin/etcd", "Path to etcd binary")
	etcdctlPath :=
		flag.String("etcdctl-bin", "./bin/etcdctl", "Path to etcdctl binary")
	address :=
		flag.String("address", "", "Binding address for scheduler and artifact server")
	driverPort :=
		flag.Int("driver-port", 0, "Binding port for scheduler driver")
	mesosAuthPrincipal :=
		flag.String("mesos-authentication-principal", "", "Mesos authentication principal")
	mesosAuthSecretFile :=
		flag.String("mesos-authentication-secret-file", "", "Mesos authentication secret file")
	authProvider :=
		flag.String("mesos-authentication-provider", sasl.ProviderName,
			fmt.Sprintf("Authentication provider to use, default is SASL that supports mechanisms: %+v", mech.ListSupported()))
	singleInstancePerSlave :=
		flag.Bool("single-instance-per-slave", true, "Only allow one etcd instance to be started per slave")
	testMode :=
		flag.Bool("test-mode", false, "disable forced values for -single-instance-per-slave and "+
			"-zk-framework-persist")
	failoverTimeoutSeconds :=
		flag.Float64("failover-timeout-seconds", 60*60*24*7, "Mesos framework failover timeout in seconds")
	weburi := flag.String("framework-weburi", "", "A URI that points to a web-based interface for interacting with the framework.")

	flag.Parse()

	if *zkFrameworkPersist == "" && !*testMode {
		log.Fatal("No value provided for -zk-framework-persist ! This can be " +
			"overridden by experts using the -test-mode=true argument, but several " +
			"runtime guarantees no longer hold, and all tasks will be orphaned when " +
			"this process exits.")
	}

	if !*singleInstancePerSlave && !*testMode {
		log.Fatal("-single-instance-per-slave=false is dangerous because it may lead to " +
			"multiple etcd instances in the same cluster on a single node, amplifying " +
			"the cost of a single node being lost, livelock, and data loss.  This can " +
			"be overridden by passing the -test-mode=true argument, at your peril.")
	}

	if *address == "" {
		hostname, err := os.Hostname()
		if err == nil {
			*address = hostname
		} else {
			log.Errorf("Could not set default binding to hostname.  Defaulting to 127.0.0.1")
			*address = "127.0.0.1"
		}
	}

	executorUris := []*mesos.CommandInfo_URI{}
	execUri, err := etcdscheduler.ServeExecutorArtifact(*executorPath, *address, *artifactPort)
	if err != nil {
		log.Errorf("Could not stat executor binary: %v", err)
		return
	}
	executorUris = append(executorUris, &mesos.CommandInfo_URI{
		Value:      execUri,
		Executable: proto.Bool(true),
	})
	etcdUri, err := etcdscheduler.ServeExecutorArtifact(*etcdPath, *address, *artifactPort)
	if err != nil {
		log.Errorf("Could not stat etcd binary: %v", err)
		return
	}
	executorUris = append(executorUris, &mesos.CommandInfo_URI{
		Value:      etcdUri,
		Executable: proto.Bool(true),
	})
	etcdctlUri, err := etcdscheduler.ServeExecutorArtifact(*etcdctlPath, *address, *artifactPort)
	if err != nil {
		log.Errorf("Could not stat etcd binary: %v", err)
		return
	}
	executorUris = append(executorUris, &mesos.CommandInfo_URI{
		Value:      etcdctlUri,
		Executable: proto.Bool(true),
	})

	go http.ListenAndServe(fmt.Sprintf("%s:%d", *address, *artifactPort), nil)
	log.V(2).Info("Serving executor artifacts...")

	bindingAddress := parseIP(*address)

	// chillFactor is the number of seconds that are slept for to allow for
	// convergence across the cluster during mutations.
	chillFactor := 10
	etcdScheduler := etcdscheduler.NewEtcdScheduler(
		*taskCount,
		chillFactor,
		*reseedTimeout,
		*autoReseed,
		executorUris,
		*singleInstancePerSlave,
		*sandboxDisk,
		*sandboxCpu,
		*sandboxMem,
	)
	etcdScheduler.ExecutorPath = *executorPath
	etcdScheduler.Master = *master
	etcdScheduler.ClusterName = *clusterName
	etcdScheduler.ZkConnect = *zkFrameworkPersist

	fwinfo := &mesos.FrameworkInfo{
		User:            proto.String(""), // Mesos-go will fill in user.
		Name:            proto.String("etcd-" + etcdScheduler.ClusterName),
		Checkpoint:      proto.Bool(true),
		FailoverTimeout: proto.Float64(*failoverTimeoutSeconds),
		WebuiUrl:        weburi,
	}

	cred := (*mesos.Credential)(nil)
	if *mesosAuthPrincipal != "" {
		fwinfo.Principal = proto.String(*mesosAuthPrincipal)
		secret, err := ioutil.ReadFile(*mesosAuthSecretFile)
		if err != nil {
			log.Fatal(err)
		}
		cred = &mesos.Credential{
			Principal: proto.String(*mesosAuthPrincipal),
			Secret:    secret,
		}
	}

	zkServers, zkChroot, err := rpc.ParseZKURI(*zkFrameworkPersist)
	etcdScheduler.ZkServers = zkServers
	etcdScheduler.ZkChroot = zkChroot
	if err != nil && *zkFrameworkPersist != "" {
		log.Fatalf("Error parsing zookeeper URI of %s: %s", *zkFrameworkPersist, err)
	} else if *zkFrameworkPersist != "" {
		previous, err := rpc.GetPreviousFrameworkID(
			zkServers,
			zkChroot,
			etcdScheduler.ClusterName,
		)
		if err != nil && err != zk.ErrNoNode {
			log.Fatalf("Could not retrieve previous framework ID: %s", err)
		} else if err == zk.ErrNoNode {
			log.Info("No previous persisted framework ID exists in zookeeper.")
		} else {
			log.Infof("Found stored framework ID in Zookeeper, "+
				"attempting to re-use: %s", previous)
			fwinfo.Id = &mesos.FrameworkID{
				Value: proto.String(previous),
			}
		}
	}

	config := scheduler.DriverConfig{
		Scheduler:      etcdScheduler,
		Framework:      fwinfo,
		Master:         etcdScheduler.Master,
		Credential:     cred,
		BindingAddress: bindingAddress,
		BindingPort:    uint16(*driverPort),
		WithAuthContext: func(ctx context.Context) context.Context {
			ctx = auth.WithLoginProvider(ctx, *authProvider)
			ctx = sasl.WithBindingAddress(ctx, bindingAddress)
			return ctx
		},
	}

	driver, err := scheduler.NewMesosSchedulerDriver(config)

	if err != nil {
		log.Errorln("Unable to create a SchedulerDriver ", err.Error())
	}

	go etcdScheduler.SerialLauncher(driver)
	go etcdScheduler.PeriodicLaunchRequestor()
	go etcdScheduler.AdminHTTP(*adminPort, driver)

	if stat, err := driver.Run(); err != nil {
		log.Infof("Framework stopped with status %s and error: %s",
			stat.String(),
			err.Error())
	}
}
<commit_msg>Use default weburi of http://<address>:<adminPort>/stats for now, until we add a simple interface that pulls this json.<commit_after>/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"os"

	"github.com/gogo/protobuf/proto"
	log "github.com/golang/glog"
	"github.com/mesos/mesos-go/auth"
	"github.com/mesos/mesos-go/auth/sasl"
	"github.com/mesos/mesos-go/auth/sasl/mech"
	mesos "github.com/mesos/mesos-go/mesosproto"
	"github.com/mesos/mesos-go/scheduler"
	"github.com/samuel/go-zookeeper/zk"
	"golang.org/x/net/context"

	"github.com/mesosphere/etcd-mesos/rpc"
	etcdscheduler "github.com/mesosphere/etcd-mesos/scheduler"
)

func parseIP(address string) net.IP {
	addr, err := net.LookupIP(address)
	if err != nil {
		log.Fatal(err)
	}
	if len(addr) < 1 {
		log.Fatalf("failed to parse IP from address '%v'", address)
	}
	return addr[0]
}

func main() {
	clusterName :=
		flag.String("cluster-name", "default", "Unique name of this etcd cluster")
	master :=
		flag.String("master", "127.0.0.1:5050", "Master address <ip:port>")
	zkFrameworkPersist :=
		flag.String("zk-framework-persist", "", "Zookeeper URI of the form zk://host1:port1,host2:port2/chroot/path")
	taskCount :=
		flag.Int("cluster-size", 5, "Total task count to run")
	adminPort :=
		flag.Int("admin-port", 23400, "Binding port for admin interface")
	reseedTimeout :=
		flag.Int("reseed-timeout", 240, "Seconds of etcd livelock to wait for before attempting a cluster re-seed")
	autoReseed :=
		flag.Bool("auto-reseed", true, "Perform automatic cluster reseed when the "+
			"cluster has been livelocked for -reseed-timeout seconds")
	artifactPort :=
		flag.Int("artifact-port", 12300, "Binding port for artifact server")
	sandboxDisk :=
		flag.Float64("sandbox-disk-limit", 4096, "Max disk usage for the etcd mesos sandbox in MB")
	sandboxCpu :=
		flag.Float64("sandbox-cpu-limit", 4, "Max cpu usage for the etcd mesos sandbox in MB")
	sandboxMem :=
		flag.Float64("sandbox-mem-limit", 2048, "Max memory usage for the etcd mesos sandbox in MB")
	executorPath :=
		flag.String("executor-bin", "./bin/etcd-mesos-executor", "Path to executor binary")
	etcdPath :=
		flag.String("etcd-bin", "./bin/etcd", "Path to etcd binary")
	etcdctlPath :=
		flag.String("etcdctl-bin", "./bin/etcdctl", "Path to etcdctl binary")
	address :=
		flag.String("address", "", "Binding address for scheduler and artifact server")
	driverPort :=
		flag.Int("driver-port", 0, "Binding port for scheduler driver")
	mesosAuthPrincipal :=
		flag.String("mesos-authentication-principal", "", "Mesos authentication principal")
	mesosAuthSecretFile :=
		flag.String("mesos-authentication-secret-file", "", "Mesos authentication secret file")
	authProvider :=
		flag.String("mesos-authentication-provider", sasl.ProviderName,
			fmt.Sprintf("Authentication provider to use, default is SASL that supports mechanisms: %+v", mech.ListSupported()))
	singleInstancePerSlave :=
		flag.Bool("single-instance-per-slave", true, "Only allow one etcd instance to be started per slave")
	testMode :=
		flag.Bool("test-mode", false, "disable forced values for -single-instance-per-slave and "+
			"-zk-framework-persist")
	failoverTimeoutSeconds :=
		flag.Float64("failover-timeout-seconds", 60*60*24*7, "Mesos framework failover timeout in seconds")
	weburi := flag.String("framework-weburi", "", "A URI that points to a web-based interface for interacting with the framework.")

	flag.Parse()

	if *zkFrameworkPersist == "" && !*testMode {
		log.Fatal("No value provided for -zk-framework-persist ! This can be " +
			"overridden by experts using the -test-mode=true argument, but several " +
			"runtime guarantees no longer hold, and all tasks will be orphaned when " +
			"this process exits.")
	}

	if !*singleInstancePerSlave && !*testMode {
		log.Fatal("-single-instance-per-slave=false is dangerous because it may lead to " +
			"multiple etcd instances in the same cluster on a single node, amplifying " +
			"the cost of a single node being lost, livelock, and data loss.  This can " +
			"be overridden by passing the -test-mode=true argument, at your peril.")
	}

	if *address == "" {
		hostname, err := os.Hostname()
		if err == nil {
			*address = hostname
		} else {
			log.Errorf("Could not set default binding to hostname.  Defaulting to 127.0.0.1")
			*address = "127.0.0.1"
		}
	}

	if *weburi == "" {
		*weburi = fmt.Sprintf("http://%s:%d/stats", *address, *adminPort)
	}

	executorUris := []*mesos.CommandInfo_URI{}
	execUri, err := etcdscheduler.ServeExecutorArtifact(*executorPath, *address, *artifactPort)
	if err != nil {
		log.Errorf("Could not stat executor binary: %v", err)
		return
	}
	executorUris = append(executorUris, &mesos.CommandInfo_URI{
		Value:      execUri,
		Executable: proto.Bool(true),
	})
	etcdUri, err := etcdscheduler.ServeExecutorArtifact(*etcdPath, *address, *artifactPort)
	if err != nil {
		log.Errorf("Could not stat etcd binary: %v", err)
		return
	}
	executorUris = append(executorUris, &mesos.CommandInfo_URI{
		Value:      etcdUri,
		Executable: proto.Bool(true),
	})
	etcdctlUri, err := etcdscheduler.ServeExecutorArtifact(*etcdctlPath, *address, *artifactPort)
	if err != nil {
		log.Errorf("Could not stat etcd binary: %v", err)
		return
	}
	executorUris = append(executorUris, &mesos.CommandInfo_URI{
		Value:      etcdctlUri,
		Executable: proto.Bool(true),
	})

	go http.ListenAndServe(fmt.Sprintf("%s:%d", *address, *artifactPort), nil)
	log.V(2).Info("Serving executor artifacts...")

	bindingAddress := parseIP(*address)

	// chillFactor is the number of seconds that are slept for to allow for
	// convergence across the cluster during mutations.
	chillFactor := 10
	etcdScheduler := etcdscheduler.NewEtcdScheduler(
		*taskCount,
		chillFactor,
		*reseedTimeout,
		*autoReseed,
		executorUris,
		*singleInstancePerSlave,
		*sandboxDisk,
		*sandboxCpu,
		*sandboxMem,
	)
	etcdScheduler.ExecutorPath = *executorPath
	etcdScheduler.Master = *master
	etcdScheduler.ClusterName = *clusterName
	etcdScheduler.ZkConnect = *zkFrameworkPersist

	fwinfo := &mesos.FrameworkInfo{
		User:            proto.String(""), // Mesos-go will fill in user.
		Name:            proto.String("etcd-" + etcdScheduler.ClusterName),
		Checkpoint:      proto.Bool(true),
		FailoverTimeout: proto.Float64(*failoverTimeoutSeconds),
		WebuiUrl:        weburi,
	}

	cred := (*mesos.Credential)(nil)
	if *mesosAuthPrincipal != "" {
		fwinfo.Principal = proto.String(*mesosAuthPrincipal)
		secret, err := ioutil.ReadFile(*mesosAuthSecretFile)
		if err != nil {
			log.Fatal(err)
		}
		cred = &mesos.Credential{
			Principal: proto.String(*mesosAuthPrincipal),
			Secret:    secret,
		}
	}

	zkServers, zkChroot, err := rpc.ParseZKURI(*zkFrameworkPersist)
	etcdScheduler.ZkServers = zkServers
	etcdScheduler.ZkChroot = zkChroot
	if err != nil && *zkFrameworkPersist != "" {
		log.Fatalf("Error parsing zookeeper URI of %s: %s", *zkFrameworkPersist, err)
	} else if *zkFrameworkPersist != "" {
		previous, err := rpc.GetPreviousFrameworkID(
			zkServers,
			zkChroot,
			etcdScheduler.ClusterName,
		)
		if err != nil && err != zk.ErrNoNode {
			log.Fatalf("Could not retrieve previous framework ID: %s", err)
		} else if err == zk.ErrNoNode {
			log.Info("No previous persisted framework ID exists in zookeeper.")
		} else {
			log.Infof("Found stored framework ID in Zookeeper, "+
				"attempting to re-use: %s", previous)
			fwinfo.Id = &mesos.FrameworkID{
				Value: proto.String(previous),
			}
		}
	}

	config := scheduler.DriverConfig{
		Scheduler:      etcdScheduler,
		Framework:      fwinfo,
		Master:         etcdScheduler.Master,
		Credential:     cred,
		BindingAddress: bindingAddress,
		BindingPort:    uint16(*driverPort),
		WithAuthContext: func(ctx context.Context) context.Context {
			ctx = auth.WithLoginProvider(ctx, *authProvider)
			ctx = sasl.WithBindingAddress(ctx, bindingAddress)
			return ctx
		},
	}

	driver, err := scheduler.NewMesosSchedulerDriver(config)

	if err != nil {
		log.Errorln("Unable to create a SchedulerDriver ", err.Error())
	}

	go etcdScheduler.SerialLauncher(driver)
	go etcdScheduler.PeriodicLaunchRequestor()
	go etcdScheduler.AdminHTTP(*adminPort, driver)

	if stat, err := driver.Run(); err != nil {
		log.Infof("Framework stopped with status %s and error: %s",
			stat.String(),
			err.Error())
	}
}
<|endoftext|>


<commit_before>package content

import (
	"fmt"
	"log"
	"strings"

	"net/http"

	"github.com/ponzu-cms/ponzu/management/editor"
	"github.com/ponzu-cms/ponzu/system/admin/user"
	"github.com/ponzu-cms/ponzu/system/item"
)

type Song struct {
	item.Item

	Title      string `json:"title"`
	Artist     string `json:"artist"`
	Rating     int    `json:"rating"`
	Opinion    string `json:"opinion"`
	SpotifyURL string `json:"spotify_url"`
}

// MarshalEditor writes a buffer of html to edit a Song within the CMS
// and implements editor.Editable
func (s *Song) MarshalEditor() ([]byte, error) {
	view, err := editor.Form(s,
		// Take note that the first argument to these Input-like functions
		// is the string version of each Song field, and must follow
		// this pattern for auto-decoding and auto-encoding reasons:
		editor.Field{
			View: editor.Input("Title", s, map[string]string{
				"label":       "Title",
				"type":        "text",
				"placeholder": "Enter the Title here",
			}),
		},
		editor.Field{
			View: editor.Input("Artist", s, map[string]string{
				"label":       "Artist",
				"type":        "text",
				"placeholder": "Enter the Artist here",
			}),
		},
		editor.Field{
			View: editor.Input("Rating", s, map[string]string{
				"label":       "Rating",
				"type":        "text",
				"placeholder": "Enter the Rating here",
			}),
		},
		editor.Field{
			View: editor.Richtext("Opinion", s, map[string]string{
				"label":       "Opinion",
				"placeholder": "Enter the Opinion here",
			}),
		},
		editor.Field{
			View: editor.Input("SpotifyURL", s, map[string]string{
				"label":       "SpotifyURL",
				"type":        "text",
				"placeholder": "Enter the SpotifyURL here",
			}),
		},
	)

	if err != nil {
		return nil, fmt.Errorf("Failed to render Song editor view: %s", err.Error())
	}

	return view, nil
}

func init() {
	item.Types["Song"] = func() interface{} { return new(Song) }
}

// String defines the display name of a Song in the CMS list-view
func (s *Song) String() string { return s.Title }

// AcceptUpdate is called after BeforeAccept and is where you may influence the
// merge process.  For example, maybe you don't want an empty string for the Title
// or Artist field to be accepted by the update request.  Updates will always merge
// with existing values, but by default will accept zero value as an update if sent.
func (s *Song) AcceptUpdate(res http.ResponseWriter, req *http.Request) error {
	addr := req.RemoteAddr
	log.Println("Song update sent by:", addr, "id:", req.URL.Query().Get("id"))

	// On update its fine if fields are missing, but we don't want
	// title overwritten by a blank or empty string since that would
	// break the display name.  Artist is also required to be non-blank.

	var required = map[string]interface{}{
		"title":  nil,
		"artist": nil,
	}

	for k, _ := range req.PostForm {
		blank := (strings.TrimSpace(req.PostFormValue(k)) == "")
		if _, ok := required[k]; ok && blank {
			log.Println("Removing blank value for:", k)
			// We'll just remove the blank values.
			// Alternately we could return an error to
			// reject the post.
			req.PostForm.Del(k)
		}
	}

	return nil
}

// BeforeAcceptUpdate is only called if the Song type implements api.Updateable
// It is called before AcceptUpdate, and returning an error will cancel the request
// causing the system to reject the data sent in the POST
func (s *Song) BeforeAcceptUpdate(res http.ResponseWriter, req *http.Request) error {
	// do initial user authentication here on the request, checking for a
	// token or cookie, or that certain form fields are set and valid

	// for example, this will check if the request was made by a CMS admin user:
	if !user.IsValid(req) {
		addr := req.RemoteAddr
		err := fmt.Errorf("request rejected, invalid user. IP: %s", addr)
		return err
	}

	// you could then to data validation on the request post form, or do it in
	// the Accept method, which is called after BeforeAccept

	return nil
}

// AfterAccept is called after Accept, and is useful for logging or triggering
// notifications, etc. after the data is saved to the database, etc.
// The request has a context containing the databse 'target' affected by the
// request. Ex. Song__pending:3 or Song:8 depending if Song implements api.Trustable
func (s *Song) AfterAccept(res http.ResponseWriter, req *http.Request) error {
	addr := req.RemoteAddr
	log.Println("Song sent by:", addr, "titled:", req.PostFormValue("title"))

	return nil
}

// AfterAcceptUpdate is called after AcceptUpdate, and is useful for logging or triggering
// notifications, etc. after the data is saved to the database, etc.
// The request has a context containing the databse 'target' affected by the
// request.
func (s *Song) AfterAcceptUpdate(res http.ResponseWriter, req *http.Request) error {
	addr := req.RemoteAddr
	log.Println("Song updated by:", addr, "titled:", req.PostFormValue("title"))

	return nil
}

// Approve implements editor.Mergeable, which enables content supplied by external
// clients to be approved and thus added to the public content API. Before content
// is approved, it is waiting in the Pending bucket, and can only be approved in
// the CMS if the Mergeable interface is satisfied. If not, you will not see this
// content show up in the CMS.
func (s *Song) Approve(res http.ResponseWriter, req *http.Request) error {
	return nil
}

/*
   NOTICE: if AutoApprove (seen below) is implemented, the Approve method above will have no
   effect, except to add the Public / Pending toggle in the CMS UI. Though, no
   Song content would be in Pending, since all externally submitting Song data
   is immediately approved.
*/

// AutoApprove implements api.Trustable, and will automatically approve content
// that has been submitted by an external client via api.Externalable. Be careful
// when using AutoApprove, because content will immediately be available through
// your public content API. If the Trustable interface is satisfied, the AfterApprove
// method is bypassed. The
func (s *Song) AutoApprove(res http.ResponseWriter, req *http.Request) error {
	// Use AutoApprove to check for trust-specific headers or whitelisted IPs,
	// etc. Remember, you will not be able to Approve or Reject content that
	// is auto-approved. You could add a field to Song, i.e.
	// AutoApproved bool `json:auto_approved`
	// and set that data here, as it is called before the content is saved, but
	// after the BeforeSave hook.

	return nil
}
<commit_msg>pulling title from PostFormValue<commit_after>package content

import (
	"fmt"
	"log"
	"strings"

	"net/http"

	"github.com/ponzu-cms/ponzu/management/editor"
	"github.com/ponzu-cms/ponzu/system/admin/user"
	"github.com/ponzu-cms/ponzu/system/item"
)

type Song struct {
	item.Item

	Title      string `json:"title"`
	Artist     string `json:"artist"`
	Rating     int    `json:"rating"`
	Opinion    string `json:"opinion"`
	SpotifyURL string `json:"spotify_url"`
}

// MarshalEditor writes a buffer of html to edit a Song within the CMS
// and implements editor.Editable
func (s *Song) MarshalEditor() ([]byte, error) {
	view, err := editor.Form(s,
		// Take note that the first argument to these Input-like functions
		// is the string version of each Song field, and must follow
		// this pattern for auto-decoding and auto-encoding reasons:
		editor.Field{
			View: editor.Input("Title", s, map[string]string{
				"label":       "Title",
				"type":        "text",
				"placeholder": "Enter the Title here",
			}),
		},
		editor.Field{
			View: editor.Input("Artist", s, map[string]string{
				"label":       "Artist",
				"type":        "text",
				"placeholder": "Enter the Artist here",
			}),
		},
		editor.Field{
			View: editor.Input("Rating", s, map[string]string{
				"label":       "Rating",
				"type":        "text",
				"placeholder": "Enter the Rating here",
			}),
		},
		editor.Field{
			View: editor.Richtext("Opinion", s, map[string]string{
				"label":       "Opinion",
				"placeholder": "Enter the Opinion here",
			}),
		},
		editor.Field{
			View: editor.Input("SpotifyURL", s, map[string]string{
				"label":       "SpotifyURL",
				"type":        "text",
				"placeholder": "Enter the SpotifyURL here",
			}),
		},
	)

	if err != nil {
		return nil, fmt.Errorf("Failed to render Song editor view: %s", err.Error())
	}

	return view, nil
}

func init() {
	item.Types["Song"] = func() interface{} { return new(Song) }
}

// String defines the display name of a Song in the CMS list-view
func (s *Song) String() string { return s.Title }

// AcceptUpdate is called after BeforeAccept and is where you may influence the
// merge process.  For example, maybe you don't want an empty string for the Title
// or Artist field to be accepted by the update request.  Updates will always merge
// with existing values, but by default will accept zero value as an update if sent.
func (s *Song) AcceptUpdate(res http.ResponseWriter, req *http.Request) error {
	addr := req.RemoteAddr
	log.Println("Song update sent by:", addr, "id:", req.URL.Query().Get("id"))

	// On update its fine if fields are missing, but we don't want
	// title overwritten by a blank or empty string since that would
	// break the display name.  Artist is also required to be non-blank.

	var required = map[string]interface{}{
		"title":  nil,
		"artist": nil,
	}

	for k, _ := range req.PostForm {
		blank := (strings.TrimSpace(req.PostFormValue(k)) == "")
		if _, ok := required[k]; ok && blank {
			log.Println("Removing blank value for:", k)
			// We'll just remove the blank values.
			// Alternately we could return an error to
			// reject the post.
			req.PostForm.Del(k)
		}
	}

	return nil
}

// BeforeAcceptUpdate is only called if the Song type implements api.Updateable
// It is called before AcceptUpdate, and returning an error will cancel the request
// causing the system to reject the data sent in the POST
func (s *Song) BeforeAcceptUpdate(res http.ResponseWriter, req *http.Request) error {
	// do initial user authentication here on the request, checking for a
	// token or cookie, or that certain form fields are set and valid

	// for example, this will check if the request was made by a CMS admin user:
	if !user.IsValid(req) {
		addr := req.RemoteAddr
		err := fmt.Errorf("request rejected, invalid user. IP: %s", addr)
		return err
	}

	// you could then to data validation on the request post form, or do it in
	// the Accept method, which is called after BeforeAccept

	return nil
}

// AfterAcceptUpdate is called after AcceptUpdate, and is useful for logging or triggering
// notifications, etc. after the data is saved to the database, etc.
// The request has a context containing the databse 'target' affected by the
// request.
func (s *Song) AfterAcceptUpdate(res http.ResponseWriter, req *http.Request) error {
	addr := req.RemoteAddr
	log.Println("Song updated by:", addr)

	return nil
}
<|endoftext|>


<commit_before>package decor

import (
	"fmt"
	"math"
	"time"
	"unicode/utf8"
)

const (
	// DidentRight specifies identation direction.
	// |foo   |b     | With DidentRight
	// |   foo|     b| Without DidentRight
	DidentRight = 1 << iota

	// DwidthSync will auto sync max width.
	// Makes sense when there're more than one bar
	DwidthSync

	// DextraSpace adds extra space, makes sense with DwidthSync only.
	// When DidentRight bit set, the space will be added to the right,
	// otherwise to the left.
	DextraSpace

	// DSyncSpace is shortcut for DwidthSync|DextraSpace
	DSyncSpace = DwidthSync | DextraSpace
)

// Statistics represents statistics of the progress bar.
// Cantains: Total, Current, TimeElapsed, TimePerItemEstimate
type Statistics struct {
	ID                  int
	Completed           bool
	Removed             bool
	Total               int64
	Current             int64
	StartTime           time.Time
	TimeElapsed         time.Duration
	TimePerItemEstimate time.Duration
}

// Eta returns exponential-weighted-moving-average ETA estimator
func (s *Statistics) Eta() time.Duration {
	return time.Duration(s.Total-s.Current) * s.TimePerItemEstimate
}

// DecoratorFunc is a function that can be prepended and appended to the progress bar
type DecoratorFunc func(s *Statistics, myWidth chan<- int, maxWidth <-chan int) string

// Name deprecated, use StaticName instead
func Name(name string, minWidth int, conf byte) DecoratorFunc {
	return StaticName(name, minWidth, conf)
}

// StaticName to be used, when there is no plan to change the name during whole
// life of a progress rendering process
func StaticName(name string, minWidth int, conf byte) DecoratorFunc {
	nameFn := func(s *Statistics) string {
		return name
	}
	return DynamicName(nameFn, minWidth, conf)
}

// DynamicName to be used, when there is a plan to change the name once or
// several times during progress rendering process. If there're more than one
// bar, and you'd like to synchronize column width, conf param should have
// DwidthSync bit set.
func DynamicName(nameFn func(*Statistics) string, minWidth int, conf byte) DecoratorFunc {
	format := "%%"
	if (conf & DidentRight) != 0 {
		format += "-"
	}
	format += "%ds"
	return func(s *Statistics, myWidth chan<- int, maxWidth <-chan int) string {
		name := nameFn(s)
		if (conf & DwidthSync) != 0 {
			myWidth <- utf8.RuneCountInString(name)
			max := <-maxWidth
			if (conf & DextraSpace) != 0 {
				max++
			}
			return fmt.Sprintf(fmt.Sprintf(format, max), name)
		}
		return fmt.Sprintf(fmt.Sprintf(format, minWidth), name)
	}
}

// CountersNoUnit returns raw counters decorator
func CountersNoUnit(pairFormat string, minWidth int, conf byte) DecoratorFunc {
	return Counters(pairFormat, 0, minWidth, conf)
}

// CountersKibiByte returns human friendly byte counters decorator, where
// counters unit is multiple by 1024.
func CountersKibiByte(pairFormat string, minWidth int, conf byte) DecoratorFunc {
	return Counters(pairFormat, Unit_KiB, minWidth, conf)
}

// CountersKiloByte returns human friendly byte counters decorator, where
// counters unit is multiple by 1000.
func CountersKiloByte(pairFormat string, minWidth int, conf byte) DecoratorFunc {
	return Counters(pairFormat, Unit_kB, minWidth, conf)
}

// Counters provides basic counters decorator.
// pairFormat must contain two printf compatible verbs, like "%f" or "%d".
// First verb substituted with Current, second one with Total. For example (assuming decor.Unit_KiB used):
// "%.1f / %.1f" = "1.0MiB / 12.0MiB" or "% .1f / % .1f" = "1.0 MiB / 12.0 MiB"
// unit is one of decor.Unit_KiB/decor.Unit_kB or just zero if you need raw unitless numbers.
func Counters(pairFormat string, unit Unit, minWidth int, conf byte) DecoratorFunc {
	format := "%%"
	if (conf & DidentRight) != 0 {
		format += "-"
	}
	format += "%ds"
	return func(s *Statistics, myWidth chan<- int, maxWidth <-chan int) string {
		var str string
		switch unit {
		case Unit_KiB:
			str = fmt.Sprintf(pairFormat, CounterKiB(s.Current), CounterKiB(s.Total))
		case Unit_kB:
			str = fmt.Sprintf(pairFormat, CounterKB(s.Current), CounterKB(s.Total))
		default:
			str = fmt.Sprintf(pairFormat, s.Current, s.Total)
		}
		if (conf & DwidthSync) != 0 {
			myWidth <- utf8.RuneCountInString(str)
			max := <-maxWidth
			if (conf & DextraSpace) != 0 {
				max++
			}
			return fmt.Sprintf(fmt.Sprintf(format, max), str)
		}
		return fmt.Sprintf(fmt.Sprintf(format, minWidth), str)
	}
}

// ETA provides exponential-weighted-moving-average ETA decorator.
// If there're more than one bar, and you'd like to synchronize column width,
// conf param should have DwidthSync bit set.
func ETA(minWidth int, conf byte) DecoratorFunc {
	format := "%%"
	if (conf & DidentRight) != 0 {
		format += "-"
	}
	format += "%ds"
	return func(s *Statistics, myWidth chan<- int, maxWidth <-chan int) string {
		str := fmt.Sprint(time.Duration(s.Eta().Seconds()) * time.Second)
		if (conf & DwidthSync) != 0 {
			myWidth <- utf8.RuneCountInString(str)
			max := <-maxWidth
			if (conf & DextraSpace) != 0 {
				max++
			}
			return fmt.Sprintf(fmt.Sprintf(format, max), str)
		}
		return fmt.Sprintf(fmt.Sprintf(format, minWidth), str)
	}
}

// Elapsed provides elapsed time decorator.
// If there're more than one bar, and you'd like to synchronize column width,
// conf param should have DwidthSync bit set.
func Elapsed(minWidth int, conf byte) DecoratorFunc {
	format := "%%"
	if (conf & DidentRight) != 0 {
		format += "-"
	}
	format += "%ds"
	return func(s *Statistics, myWidth chan<- int, maxWidth <-chan int) string {
		str := fmt.Sprint(time.Duration(s.TimeElapsed.Seconds()) * time.Second)
		if (conf & DwidthSync) != 0 {
			myWidth <- utf8.RuneCountInString(str)
			max := <-maxWidth
			if (conf & DextraSpace) != 0 {
				max++
			}
			return fmt.Sprintf(fmt.Sprintf(format, max), str)
		}
		return fmt.Sprintf(fmt.Sprintf(format, minWidth), str)
	}
}

// Percentage provides percentage decorator.
// If there're more than one bar, and you'd like to synchronize column width,
// conf param should have DwidthSync bit set.
func Percentage(minWidth int, conf byte) DecoratorFunc {
	format := "%%"
	if (conf & DidentRight) != 0 {
		format += "-"
	}
	format += "%ds"
	return func(s *Statistics, myWidth chan<- int, maxWidth <-chan int) string {
		str := fmt.Sprintf("%d %%", CalcPercentage(s.Total, s.Current, 100))
		if (conf & DwidthSync) != 0 {
			myWidth <- utf8.RuneCountInString(str)
			max := <-maxWidth
			if (conf & DextraSpace) != 0 {
				max++
			}
			return fmt.Sprintf(fmt.Sprintf(format, max), str)
		}
		return fmt.Sprintf(fmt.Sprintf(format, minWidth), str)
	}
}

func CalcPercentage(total, current, width int64) (perc int64) {
	if total <= 0 {
		return 0
	}
	if current > total {
		current = total
	}

	num := float64(width) * float64(current) / float64(total)
	ceil := math.Ceil(num)
	diff := ceil - num
	// num = 2.34 will return 2
	// num = 2.44 will return 3
	if math.Max(diff, 0.6) == diff {
		return int64(num)
	}
	return int64(ceil)
}
<commit_msg>decorators: more descriptive params<commit_after>package decor

import (
	"fmt"
	"math"
	"time"
	"unicode/utf8"
)

const (
	// DidentRight specifies identation direction.
	// |foo   |b     | With DidentRight
	// |   foo|     b| Without DidentRight
	DidentRight = 1 << iota

	// DwidthSync will auto sync max width.
	// Makes sense when there're more than one bar
	DwidthSync

	// DextraSpace adds extra space, makes sense with DwidthSync only.
	// When DidentRight bit set, the space will be added to the right,
	// otherwise to the left.
	DextraSpace

	// DSyncSpace is shortcut for DwidthSync|DextraSpace
	DSyncSpace = DwidthSync | DextraSpace
)

// Statistics represents statistics of the progress bar.
// Cantains: Total, Current, TimeElapsed, TimePerItemEstimate
type Statistics struct {
	ID                  int
	Completed           bool
	Removed             bool
	Total               int64
	Current             int64
	StartTime           time.Time
	TimeElapsed         time.Duration
	TimePerItemEstimate time.Duration
}

// Eta returns exponential-weighted-moving-average ETA estimator
func (s *Statistics) Eta() time.Duration {
	return time.Duration(s.Total-s.Current) * s.TimePerItemEstimate
}

// DecoratorFunc is a function that can be prepended and appended to the progress bar
type DecoratorFunc func(s *Statistics, widthAccumulator chan<- int, widthDistributor <-chan int) string

// Name deprecated, use StaticName instead
func Name(name string, minWidth int, conf byte) DecoratorFunc {
	return StaticName(name, minWidth, conf)
}

// StaticName to be used, when there is no plan to change the name during whole
// life of a progress rendering process
func StaticName(name string, minWidth int, conf byte) DecoratorFunc {
	nameFn := func(s *Statistics) string {
		return name
	}
	return DynamicName(nameFn, minWidth, conf)
}

// DynamicName to be used, when there is a plan to change the name once or
// several times during progress rendering process. If there're more than one
// bar, and you'd like to synchronize column width, conf param should have
// DwidthSync bit set.
func DynamicName(nameFn func(*Statistics) string, minWidth int, conf byte) DecoratorFunc {
	format := "%%"
	if (conf & DidentRight) != 0 {
		format += "-"
	}
	format += "%ds"
	return func(s *Statistics, widthAccumulator chan<- int, widthDistributor <-chan int) string {
		name := nameFn(s)
		if (conf & DwidthSync) != 0 {
			widthAccumulator <- utf8.RuneCountInString(name)
			max := <-widthDistributor
			if (conf & DextraSpace) != 0 {
				max++
			}
			return fmt.Sprintf(fmt.Sprintf(format, max), name)
		}
		return fmt.Sprintf(fmt.Sprintf(format, minWidth), name)
	}
}

// CountersNoUnit returns raw counters decorator
func CountersNoUnit(pairFormat string, minWidth int, conf byte) DecoratorFunc {
	return Counters(pairFormat, 0, minWidth, conf)
}

// CountersKibiByte returns human friendly byte counters decorator, where
// counters unit is multiple by 1024.
func CountersKibiByte(pairFormat string, minWidth int, conf byte) DecoratorFunc {
	return Counters(pairFormat, Unit_KiB, minWidth, conf)
}

// CountersKiloByte returns human friendly byte counters decorator, where
// counters unit is multiple by 1000.
func CountersKiloByte(pairFormat string, minWidth int, conf byte) DecoratorFunc {
	return Counters(pairFormat, Unit_kB, minWidth, conf)
}

// Counters provides basic counters decorator.
// pairFormat must contain two printf compatible verbs, like "%f" or "%d".
// First verb substituted with Current, second one with Total. For example (assuming decor.Unit_KiB used):
// "%.1f / %.1f" = "1.0MiB / 12.0MiB" or "% .1f / % .1f" = "1.0 MiB / 12.0 MiB"
// unit is one of decor.Unit_KiB/decor.Unit_kB or just zero if you need raw unitless numbers.
func Counters(pairFormat string, unit Unit, minWidth int, conf byte) DecoratorFunc {
	format := "%%"
	if (conf & DidentRight) != 0 {
		format += "-"
	}
	format += "%ds"
	return func(s *Statistics, widthAccumulator chan<- int, widthDistributor <-chan int) string {
		var str string
		switch unit {
		case Unit_KiB:
			str = fmt.Sprintf(pairFormat, CounterKiB(s.Current), CounterKiB(s.Total))
		case Unit_kB:
			str = fmt.Sprintf(pairFormat, CounterKB(s.Current), CounterKB(s.Total))
		default:
			str = fmt.Sprintf(pairFormat, s.Current, s.Total)
		}
		if (conf & DwidthSync) != 0 {
			widthAccumulator <- utf8.RuneCountInString(str)
			max := <-widthDistributor
			if (conf & DextraSpace) != 0 {
				max++
			}
			return fmt.Sprintf(fmt.Sprintf(format, max), str)
		}
		return fmt.Sprintf(fmt.Sprintf(format, minWidth), str)
	}
}

// ETA provides exponential-weighted-moving-average ETA decorator.
// If there're more than one bar, and you'd like to synchronize column width,
// conf param should have DwidthSync bit set.
func ETA(minWidth int, conf byte) DecoratorFunc {
	format := "%%"
	if (conf & DidentRight) != 0 {
		format += "-"
	}
	format += "%ds"
	return func(s *Statistics, widthAccumulator chan<- int, widthDistributor <-chan int) string {
		str := fmt.Sprint(time.Duration(s.Eta().Seconds()) * time.Second)
		if (conf & DwidthSync) != 0 {
			widthAccumulator <- utf8.RuneCountInString(str)
			max := <-widthDistributor
			if (conf & DextraSpace) != 0 {
				max++
			}
			return fmt.Sprintf(fmt.Sprintf(format, max), str)
		}
		return fmt.Sprintf(fmt.Sprintf(format, minWidth), str)
	}
}

// Elapsed provides elapsed time decorator.
// If there're more than one bar, and you'd like to synchronize column width,
// conf param should have DwidthSync bit set.
func Elapsed(minWidth int, conf byte) DecoratorFunc {
	format := "%%"
	if (conf & DidentRight) != 0 {
		format += "-"
	}
	format += "%ds"
	return func(s *Statistics, widthAccumulator chan<- int, widthDistributor <-chan int) string {
		str := fmt.Sprint(time.Duration(s.TimeElapsed.Seconds()) * time.Second)
		if (conf & DwidthSync) != 0 {
			widthAccumulator <- utf8.RuneCountInString(str)
			max := <-widthDistributor
			if (conf & DextraSpace) != 0 {
				max++
			}
			return fmt.Sprintf(fmt.Sprintf(format, max), str)
		}
		return fmt.Sprintf(fmt.Sprintf(format, minWidth), str)
	}
}

// Percentage provides percentage decorator.
// If there're more than one bar, and you'd like to synchronize column width,
// conf param should have DwidthSync bit set.
func Percentage(minWidth int, conf byte) DecoratorFunc {
	format := "%%"
	if (conf & DidentRight) != 0 {
		format += "-"
	}
	format += "%ds"
	return func(s *Statistics, widthAccumulator chan<- int, widthDistributor <-chan int) string {
		str := fmt.Sprintf("%d %%", CalcPercentage(s.Total, s.Current, 100))
		if (conf & DwidthSync) != 0 {
			widthAccumulator <- utf8.RuneCountInString(str)
			max := <-widthDistributor
			if (conf & DextraSpace) != 0 {
				max++
			}
			return fmt.Sprintf(fmt.Sprintf(format, max), str)
		}
		return fmt.Sprintf(fmt.Sprintf(format, minWidth), str)
	}
}

func CalcPercentage(total, current, width int64) (perc int64) {
	if total <= 0 {
		return 0
	}
	if current > total {
		current = total
	}

	num := float64(width) * float64(current) / float64(total)
	ceil := math.Ceil(num)
	diff := ceil - num
	// num = 2.34 will return 2
	// num = 2.44 will return 3
	if math.Max(diff, 0.6) == diff {
		return int64(num)
	}
	return int64(ceil)
}
<|endoftext|>


<commit_before>package ingester

import (
	"strconv"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
)

type item int64

func (i item) Priority() int64 {
	return int64(i)
}

func (i item) Key() string {
	return strconv.FormatInt(int64(i), 10)
}

func TestPriorityQueueBasic(t *testing.T) {
	queue := newPriorityQueue()
	assert.Equal(t, 0, queue.Length(), "Expected length = 0")

	queue.Enqueue(item(1))
	assert.Equal(t, 1, queue.Length(), "Expected length = 1")

	i, ok := queue.Dequeue().(item)
	assert.True(t, ok, "Expected cast to succeed")
	assert.Equal(t, item(1), i, "Expected to dequeue item(1)")

	queue.Close()
	assert.Nil(t, queue.Dequeue(), "Expect nil dequeue")
}

func TestPriorityQueuePriorities(t *testing.T) {
	queue := newPriorityQueue()
	queue.Enqueue(item(1))
	queue.Enqueue(item(2))

	assert.Equal(t, item(2), queue.Dequeue().(item), "Expected to dequeue item(2)")
	assert.Equal(t, item(1), queue.Dequeue().(item), "Expected to dequeue item(1)")

	queue.Close()
	assert.Nil(t, queue.Dequeue(), "Expect nil dequeue")
}

func TestPriorityQueuePriorities2(t *testing.T) {
	queue := newPriorityQueue()
	queue.Enqueue(item(2))
	queue.Enqueue(item(1))

	assert.Equal(t, item(2), queue.Dequeue().(item), "Expected to dequeue item(2)")
	assert.Equal(t, item(1), queue.Dequeue().(item), "Expected to dequeue item(1)")

	queue.Close()
	assert.Nil(t, queue.Dequeue(), "Expect nil dequeue")
}

func TestPriorityQueueDedupe(t *testing.T) {
	queue := newPriorityQueue()
	queue.Enqueue(item(1))
	queue.Enqueue(item(1))

	assert.Equal(t, 1, queue.Length(), "Expected length = 1")
	assert.Equal(t, item(1), queue.Dequeue().(item), "Expected to dequeue item(1)")

	queue.Close()
	assert.Nil(t, queue.Dequeue(), "Expect nil dequeue")
}

func TestPriorityQueueWait(t *testing.T) {
	queue := newPriorityQueue()

	done := make(chan struct{})
	go func() {
		assert.Nil(t, queue.Dequeue(), "Expect nil dequeue")
		close(done)
	}()

	queue.Close()
	select {
	case <-done:
	case <-time.After(100 * time.Millisecond):
		t.Fatal("Close didn't unblock Dequeue.")
	}
}
<commit_msg>Add runtime.Gosched() to concurrency test<commit_after>package ingester

import (
	"runtime"
	"strconv"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
)

type item int64

func (i item) Priority() int64 {
	return int64(i)
}

func (i item) Key() string {
	return strconv.FormatInt(int64(i), 10)
}

func TestPriorityQueueBasic(t *testing.T) {
	queue := newPriorityQueue()
	assert.Equal(t, 0, queue.Length(), "Expected length = 0")

	queue.Enqueue(item(1))
	assert.Equal(t, 1, queue.Length(), "Expected length = 1")

	i, ok := queue.Dequeue().(item)
	assert.True(t, ok, "Expected cast to succeed")
	assert.Equal(t, item(1), i, "Expected to dequeue item(1)")

	queue.Close()
	assert.Nil(t, queue.Dequeue(), "Expect nil dequeue")
}

func TestPriorityQueuePriorities(t *testing.T) {
	queue := newPriorityQueue()
	queue.Enqueue(item(1))
	queue.Enqueue(item(2))

	assert.Equal(t, item(2), queue.Dequeue().(item), "Expected to dequeue item(2)")
	assert.Equal(t, item(1), queue.Dequeue().(item), "Expected to dequeue item(1)")

	queue.Close()
	assert.Nil(t, queue.Dequeue(), "Expect nil dequeue")
}

func TestPriorityQueuePriorities2(t *testing.T) {
	queue := newPriorityQueue()
	queue.Enqueue(item(2))
	queue.Enqueue(item(1))

	assert.Equal(t, item(2), queue.Dequeue().(item), "Expected to dequeue item(2)")
	assert.Equal(t, item(1), queue.Dequeue().(item), "Expected to dequeue item(1)")

	queue.Close()
	assert.Nil(t, queue.Dequeue(), "Expect nil dequeue")
}

func TestPriorityQueueDedupe(t *testing.T) {
	queue := newPriorityQueue()
	queue.Enqueue(item(1))
	queue.Enqueue(item(1))

	assert.Equal(t, 1, queue.Length(), "Expected length = 1")
	assert.Equal(t, item(1), queue.Dequeue().(item), "Expected to dequeue item(1)")

	queue.Close()
	assert.Nil(t, queue.Dequeue(), "Expect nil dequeue")
}

func TestPriorityQueueWait(t *testing.T) {
	queue := newPriorityQueue()

	done := make(chan struct{})
	go func() {
		assert.Nil(t, queue.Dequeue(), "Expect nil dequeue")
		close(done)
	}()

	runtime.Gosched()
	queue.Close()
	select {
	case <-done:
	case <-time.After(100 * time.Millisecond):
		t.Fatal("Close didn't unblock Dequeue.")
	}
}
<|endoftext|>


<commit_before>package chroot

import (
	"context"
	"errors"
	"fmt"
	"log"

	osccommon "github.com/hashicorp/packer/builder/osc/common"
	"github.com/hashicorp/packer/helper/multistep"
	"github.com/hashicorp/packer/packer"
	"github.com/hashicorp/packer/template/interpolate"
	"github.com/outscale/osc-go/oapi"
)

// StepCreateVolume creates a new volume from the snapshot of the root
// device of the OMI.
//
// Produces:
//   volume_id string - The ID of the created volume
type StepCreateVolume struct {
	volumeId       string
	RootVolumeSize int64
	RootVolumeType string
	RootVolumeTags osccommon.TagMap
	Ctx            interpolate.Context
}

func (s *StepCreateVolume) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
	config := state.Get("config").(*Config)
	oapiconn := state.Get("oapi").(*oapi.Client)
	vm := state.Get("vm").(oapi.Vm)
	ui := state.Get("ui").(packer.Ui)

	var err error

	volTags, err := s.RootVolumeTags.OAPITags(s.Ctx, oapiconn.GetConfig().Region, state)

	if err != nil {
		state.Put("error", err)
		ui.Error(err.Error())
		return multistep.ActionHalt
	}

	var createVolume *oapi.CreateVolumeRequest
	if config.FromScratch {
		rootVolumeType := osccommon.VolumeTypeGp2
		if s.RootVolumeType == "io1" {
			err := errors.New("Cannot use io1 volume when building from scratch")
			state.Put("error", err)
			ui.Error(err.Error())
			return multistep.ActionHalt
		} else if s.RootVolumeType != "" {
			rootVolumeType = s.RootVolumeType
		}
		createVolume = &oapi.CreateVolumeRequest{
			SubregionName: vm.Placement.SubregionName,
			Size:          s.RootVolumeSize,
			VolumeType:    rootVolumeType,
		}

	} else {
		// Determine the root device snapshot
		image := state.Get("source_image").(oapi.Image)
		log.Printf("Searching for root device of the image (%s)", image.RootDeviceName)
		var rootDevice *oapi.BlockDeviceMappingImage
		for _, device := range image.BlockDeviceMappings {
			if device.DeviceName == image.RootDeviceName {
				rootDevice = &device
				break
			}
		}

		ui.Say("Creating the root volume...")
		createVolume, err = s.buildCreateVolumeInput(vm.Placement.SubregionName, rootDevice)
		if err != nil {
			state.Put("error", err)
			ui.Error(err.Error())
			return multistep.ActionHalt
		}
	}

	log.Printf("Create args: %+v", createVolume)

	createVolumeResp, err := oapiconn.POST_CreateVolume(*createVolume)
	if err != nil {
		err := fmt.Errorf("Error creating root volume: %s", err)
		state.Put("error", err)
		ui.Error(err.Error())
		return multistep.ActionHalt
	}

	// Set the volume ID so we remember to delete it later
	s.volumeId = createVolumeResp.OK.Volume.VolumeId
	log.Printf("Volume ID: %s", s.volumeId)

	//Create tags for volume
	if len(volTags) > 0 {
		osccommon.CreateTags(oapiconn, s.volumeId, ui, volTags)
	}

	// Wait for the volume to become ready
	err = osccommon.WaitUntilVolumeAvailable(oapiconn, s.volumeId)
	if err != nil {
		err := fmt.Errorf("Error waiting for volume: %s", err)
		state.Put("error", err)
		ui.Error(err.Error())
		return multistep.ActionHalt
	}

	state.Put("volume_id", s.volumeId)
	return multistep.ActionContinue
}

func (s *StepCreateVolume) Cleanup(state multistep.StateBag) {
	if s.volumeId == "" {
		return
	}

	oapiconn := state.Get("oapi").(*oapi.Client)
	ui := state.Get("ui").(packer.Ui)

	ui.Say("Deleting the created BSU volume...")
	_, err := oapiconn.POST_DeleteVolume(oapi.DeleteVolumeRequest{VolumeId: s.volumeId})
	if err != nil {
		ui.Error(fmt.Sprintf("Error deleting BSU volume: %s", err))
	}
}

func (s *StepCreateVolume) buildCreateVolumeInput(suregionName string, rootDevice *oapi.BlockDeviceMappingImage) (*oapi.CreateVolumeRequest, error) {
	if rootDevice == nil {
		return nil, fmt.Errorf("Couldn't find root device!")
	}

	//FIX: Temporary fix
	gibSize := rootDevice.Bsu.VolumeSize / (1024 * 1024 * 1024)
	createVolumeInput := &oapi.CreateVolumeRequest{
		SubregionName: suregionName,
		Size:          gibSize,
		SnapshotId:    rootDevice.Bsu.SnapshotId,
		VolumeType:    rootDevice.Bsu.VolumeType,
		Iops:          rootDevice.Bsu.Iops,
	}
	if s.RootVolumeSize > rootDevice.Bsu.VolumeSize {
		createVolumeInput.Size = s.RootVolumeSize
	}

	if s.RootVolumeType == "" || s.RootVolumeType == rootDevice.Bsu.VolumeType {
		return createVolumeInput, nil
	}

	if s.RootVolumeType == "io1" {
		return nil, fmt.Errorf("Root volume type cannot be io1, because existing root volume type was %s", rootDevice.Bsu.VolumeType)
	}

	createVolumeInput.VolumeType = s.RootVolumeType
	// non io1 cannot set iops
	createVolumeInput.Iops = 0

	return createVolumeInput, nil
}
<commit_msg>refactor: add err check in create tags<commit_after>package chroot

import (
	"context"
	"errors"
	"fmt"
	"log"

	osccommon "github.com/hashicorp/packer/builder/osc/common"
	"github.com/hashicorp/packer/helper/multistep"
	"github.com/hashicorp/packer/packer"
	"github.com/hashicorp/packer/template/interpolate"
	"github.com/outscale/osc-go/oapi"
)

// StepCreateVolume creates a new volume from the snapshot of the root
// device of the OMI.
//
// Produces:
//   volume_id string - The ID of the created volume
type StepCreateVolume struct {
	volumeId       string
	RootVolumeSize int64
	RootVolumeType string
	RootVolumeTags osccommon.TagMap
	Ctx            interpolate.Context
}

func (s *StepCreateVolume) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
	config := state.Get("config").(*Config)
	oapiconn := state.Get("oapi").(*oapi.Client)
	vm := state.Get("vm").(oapi.Vm)
	ui := state.Get("ui").(packer.Ui)

	var err error

	volTags, err := s.RootVolumeTags.OAPITags(s.Ctx, oapiconn.GetConfig().Region, state)

	if err != nil {
		state.Put("error", err)
		ui.Error(err.Error())
		return multistep.ActionHalt
	}

	var createVolume *oapi.CreateVolumeRequest
	if config.FromScratch {
		rootVolumeType := osccommon.VolumeTypeGp2
		if s.RootVolumeType == "io1" {
			err := errors.New("Cannot use io1 volume when building from scratch")
			state.Put("error", err)
			ui.Error(err.Error())
			return multistep.ActionHalt
		} else if s.RootVolumeType != "" {
			rootVolumeType = s.RootVolumeType
		}
		createVolume = &oapi.CreateVolumeRequest{
			SubregionName: vm.Placement.SubregionName,
			Size:          s.RootVolumeSize,
			VolumeType:    rootVolumeType,
		}

	} else {
		// Determine the root device snapshot
		image := state.Get("source_image").(oapi.Image)
		log.Printf("Searching for root device of the image (%s)", image.RootDeviceName)
		var rootDevice *oapi.BlockDeviceMappingImage
		for _, device := range image.BlockDeviceMappings {
			if device.DeviceName == image.RootDeviceName {
				rootDevice = &device
				break
			}
		}

		ui.Say("Creating the root volume...")
		createVolume, err = s.buildCreateVolumeInput(vm.Placement.SubregionName, rootDevice)
		if err != nil {
			state.Put("error", err)
			ui.Error(err.Error())
			return multistep.ActionHalt
		}
	}

	log.Printf("Create args: %+v", createVolume)

	createVolumeResp, err := oapiconn.POST_CreateVolume(*createVolume)
	if err != nil {
		err := fmt.Errorf("Error creating root volume: %s", err)
		state.Put("error", err)
		ui.Error(err.Error())
		return multistep.ActionHalt
	}

	// Set the volume ID so we remember to delete it later
	s.volumeId = createVolumeResp.OK.Volume.VolumeId
	log.Printf("Volume ID: %s", s.volumeId)

	//Create tags for volume
	if len(volTags) > 0 {
		if err := osccommon.CreateTags(oapiconn, s.volumeId, ui, volTags); err != nil {
			err := fmt.Errorf("Error creating tags for volume: %s", err)
			state.Put("error", err)
			ui.Error(err.Error())
			return multistep.ActionHalt
		}
	}

	// Wait for the volume to become ready
	err = osccommon.WaitUntilVolumeAvailable(oapiconn, s.volumeId)
	if err != nil {
		err := fmt.Errorf("Error waiting for volume: %s", err)
		state.Put("error", err)
		ui.Error(err.Error())
		return multistep.ActionHalt
	}

	state.Put("volume_id", s.volumeId)
	return multistep.ActionContinue
}

func (s *StepCreateVolume) Cleanup(state multistep.StateBag) {
	if s.volumeId == "" {
		return
	}

	oapiconn := state.Get("oapi").(*oapi.Client)
	ui := state.Get("ui").(packer.Ui)

	ui.Say("Deleting the created BSU volume...")
	_, err := oapiconn.POST_DeleteVolume(oapi.DeleteVolumeRequest{VolumeId: s.volumeId})
	if err != nil {
		ui.Error(fmt.Sprintf("Error deleting BSU volume: %s", err))
	}
}

func (s *StepCreateVolume) buildCreateVolumeInput(suregionName string, rootDevice *oapi.BlockDeviceMappingImage) (*oapi.CreateVolumeRequest, error) {
	if rootDevice == nil {
		return nil, fmt.Errorf("Couldn't find root device!")
	}

	//FIX: Temporary fix
	gibSize := rootDevice.Bsu.VolumeSize / (1024 * 1024 * 1024)
	createVolumeInput := &oapi.CreateVolumeRequest{
		SubregionName: suregionName,
		Size:          gibSize,
		SnapshotId:    rootDevice.Bsu.SnapshotId,
		VolumeType:    rootDevice.Bsu.VolumeType,
		Iops:          rootDevice.Bsu.Iops,
	}
	if s.RootVolumeSize > rootDevice.Bsu.VolumeSize {
		createVolumeInput.Size = s.RootVolumeSize
	}

	if s.RootVolumeType == "" || s.RootVolumeType == rootDevice.Bsu.VolumeType {
		return createVolumeInput, nil
	}

	if s.RootVolumeType == "io1" {
		return nil, fmt.Errorf("Root volume type cannot be io1, because existing root volume type was %s", rootDevice.Bsu.VolumeType)
	}

	createVolumeInput.VolumeType = s.RootVolumeType
	// non io1 cannot set iops
	createVolumeInput.Iops = 0

	return createVolumeInput, nil
}
<|endoftext|>


<commit_before>package chroot

import (
	"context"
	"errors"
	"fmt"
	"log"

	osccommon "github.com/hashicorp/packer/builder/osc/common"
	"github.com/hashicorp/packer/helper/multistep"
	"github.com/hashicorp/packer/packer"
	"github.com/hashicorp/packer/template/interpolate"
	"github.com/outscale/osc-go/oapi"
)

// StepCreateVolume creates a new volume from the snapshot of the root
// device of the OMI.
//
// Produces:
//   volume_id string - The ID of the created volume
type StepCreateVolume struct {
	volumeId       string
	RootVolumeSize int64
	RootVolumeType string
	RootVolumeTags osccommon.TagMap
	Ctx            interpolate.Context
}

func (s *StepCreateVolume) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
	config := state.Get("config").(*Config)
	oapiconn := state.Get("oapi").(*oapi.Client)
	vm := state.Get("vm").(oapi.Vm)
	ui := state.Get("ui").(packer.Ui)

	var err error

	//TODO: Add tags
	// volTags, err := s.RootVolumeTags.OAPITags(s.Ctx, oapiconn.GetConfig().Region, state)
	// if err != nil {
	// 	err := fmt.Errorf("Error tagging volumes: %s", err)
	// 	state.Put("error", err)
	// 	ui.Error(err.Error())
	// 	return multistep.ActionHalt
	// }

	// // Collect tags for tagging on resource creation
	// var tagSpecs []oapi.ResourceTag

	// if len(volTags) > 0 {
	// 	runVolTags := &oapi.Resou rceTag{
	// 		ResourceType: "volume",
	// 		Tags:         volTags,
	// 	}

	// 	tagSpecs = append(tagSpecs, runVolTags)
	// }

	var createVolume *oapi.CreateVolumeRequest
	if config.FromScratch {
		rootVolumeType := osccommon.VolumeTypeGp2
		if s.RootVolumeType == "io1" {
			err := errors.New("Cannot use io1 volume when building from scratch")
			state.Put("error", err)
			ui.Error(err.Error())
			return multistep.ActionHalt
		} else if s.RootVolumeType != "" {
			rootVolumeType = s.RootVolumeType
		}
		createVolume = &oapi.CreateVolumeRequest{
			SubregionName: vm.Placement.SubregionName,
			Size:          s.RootVolumeSize,
			VolumeType:    rootVolumeType,
		}

	} else {
		// Determine the root device snapshot
		image := state.Get("source_image").(oapi.Image)
		log.Printf("Searching for root device of the image (%s)", image.RootDeviceName)
		var rootDevice *oapi.BlockDeviceMappingImage
		for _, device := range image.BlockDeviceMappings {
			if device.DeviceName == image.RootDeviceName {
				*rootDevice = device
				break
			}
		}

		ui.Say("Creating the root volume...")
		createVolume, err = s.buildCreateVolumeInput(vm.Placement.SubregionName, rootDevice)
		if err != nil {
			state.Put("error", err)
			ui.Error(err.Error())
			return multistep.ActionHalt
		}
	}
	//TODO: ADD TAGS
	// if len(tagSpecs) > 0 {
	// 	createVolume.SetTagSpecifications(tagSpecs)
	// 	volTags.Report(ui)
	// }
	log.Printf("Create args: %+v", createVolume)

	createVolumeResp, err := oapiconn.POST_CreateVolume(*createVolume)
	if err != nil {
		err := fmt.Errorf("Error creating root volume: %s", err)
		state.Put("error", err)
		ui.Error(err.Error())
		return multistep.ActionHalt
	}

	// Set the volume ID so we remember to delete it later
	s.volumeId = createVolumeResp.OK.Volume.VolumeId
	log.Printf("Volume ID: %s", s.volumeId)

	// Wait for the volume to become ready
	err = osccommon.WaitUntilVolumeAvailable(oapiconn, s.volumeId)
	if err != nil {
		err := fmt.Errorf("Error waiting for volume: %s", err)
		state.Put("error", err)
		ui.Error(err.Error())
		return multistep.ActionHalt
	}

	state.Put("volume_id", s.volumeId)
	return multistep.ActionContinue
}

func (s *StepCreateVolume) Cleanup(state multistep.StateBag) {
	if s.volumeId == "" {
		return
	}

	oapiconn := state.Get("oapi").(*oapi.Client)
	ui := state.Get("ui").(packer.Ui)

	ui.Say("Deleting the created BSU volume...")
	_, err := oapiconn.POST_DeleteVolume(oapi.DeleteVolumeRequest{VolumeId: s.volumeId})
	if err != nil {
		ui.Error(fmt.Sprintf("Error deleting BSU volume: %s", err))
	}
}

func (s *StepCreateVolume) buildCreateVolumeInput(suregionName string, rootDevice *oapi.BlockDeviceMappingImage) (*oapi.CreateVolumeRequest, error) {
	if rootDevice == nil {
		return nil, fmt.Errorf("Couldn't find root device!")
	}
	createVolumeInput := &oapi.CreateVolumeRequest{
		SubregionName: suregionName,
		Size:          rootDevice.Bsu.VolumeSize,
		SnapshotId:    rootDevice.Bsu.SnapshotId,
		VolumeType:    rootDevice.Bsu.VolumeType,
		Iops:          rootDevice.Bsu.Iops,
	}
	if s.RootVolumeSize > rootDevice.Bsu.VolumeSize {
		createVolumeInput.Size = s.RootVolumeSize
	}

	if s.RootVolumeType == "" || s.RootVolumeType == rootDevice.Bsu.VolumeType {
		return createVolumeInput, nil
	}

	if s.RootVolumeType == "io1" {
		return nil, fmt.Errorf("Root volume type cannot be io1, because existing root volume type was %s", rootDevice.Bsu.VolumeType)
	}

	createVolumeInput.VolumeType = s.RootVolumeType
	// non io1 cannot set iops
	createVolumeInput.Iops = 0

	return createVolumeInput, nil
}
<commit_msg>fix: convert volume size to Gib before volume creation<commit_after>package chroot

import (
	"context"
	"errors"
	"fmt"
	"log"

	osccommon "github.com/hashicorp/packer/builder/osc/common"
	"github.com/hashicorp/packer/helper/multistep"
	"github.com/hashicorp/packer/packer"
	"github.com/hashicorp/packer/template/interpolate"
	"github.com/outscale/osc-go/oapi"
)

// StepCreateVolume creates a new volume from the snapshot of the root
// device of the OMI.
//
// Produces:
//   volume_id string - The ID of the created volume
type StepCreateVolume struct {
	volumeId       string
	RootVolumeSize int64
	RootVolumeType string
	RootVolumeTags osccommon.TagMap
	Ctx            interpolate.Context
}

func (s *StepCreateVolume) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
	config := state.Get("config").(*Config)
	oapiconn := state.Get("oapi").(*oapi.Client)
	vm := state.Get("vm").(oapi.Vm)
	ui := state.Get("ui").(packer.Ui)

	var err error

	//TODO: Add tags
	// volTags, err := s.RootVolumeTags.OAPITags(s.Ctx, oapiconn.GetConfig().Region, state)
	// if err != nil {
	// 	err := fmt.Errorf("Error tagging volumes: %s", err)
	// 	state.Put("error", err)
	// 	ui.Error(err.Error())
	// 	return multistep.ActionHalt
	// }

	// // Collect tags for tagging on resource creation
	// var tagSpecs []oapi.ResourceTag

	// if len(volTags) > 0 {
	// 	runVolTags := &oapi.Resou rceTag{
	// 		ResourceType: "volume",
	// 		Tags:         volTags,
	// 	}

	// 	tagSpecs = append(tagSpecs, runVolTags)
	// }

	var createVolume *oapi.CreateVolumeRequest
	if config.FromScratch {
		rootVolumeType := osccommon.VolumeTypeGp2
		if s.RootVolumeType == "io1" {
			err := errors.New("Cannot use io1 volume when building from scratch")
			state.Put("error", err)
			ui.Error(err.Error())
			return multistep.ActionHalt
		} else if s.RootVolumeType != "" {
			rootVolumeType = s.RootVolumeType
		}
		createVolume = &oapi.CreateVolumeRequest{
			SubregionName: vm.Placement.SubregionName,
			Size:          s.RootVolumeSize,
			VolumeType:    rootVolumeType,
		}

	} else {
		// Determine the root device snapshot
		image := state.Get("source_image").(oapi.Image)
		log.Printf("Searching for root device of the image (%s)", image.RootDeviceName)
		var rootDevice *oapi.BlockDeviceMappingImage
		for _, device := range image.BlockDeviceMappings {
			if device.DeviceName == image.RootDeviceName {
				rootDevice = &device
				break
			}
		}

		ui.Say("Creating the root volume...")
		createVolume, err = s.buildCreateVolumeInput(vm.Placement.SubregionName, rootDevice)
		if err != nil {
			state.Put("error", err)
			ui.Error(err.Error())
			return multistep.ActionHalt
		}
	}
	//TODO: ADD TAGS
	// if len(tagSpecs) > 0 {
	// 	createVolume.SetTagSpecifications(tagSpecs)
	// 	volTags.Report(ui)
	// }
	log.Printf("Create args: %+v", createVolume)

	createVolumeResp, err := oapiconn.POST_CreateVolume(*createVolume)
	if err != nil {
		err := fmt.Errorf("Error creating root volume: %s", err)
		state.Put("error", err)
		ui.Error(err.Error())
		return multistep.ActionHalt
	}

	// Set the volume ID so we remember to delete it later
	s.volumeId = createVolumeResp.OK.Volume.VolumeId
	log.Printf("Volume ID: %s", s.volumeId)

	// Wait for the volume to become ready
	err = osccommon.WaitUntilVolumeAvailable(oapiconn, s.volumeId)
	if err != nil {
		err := fmt.Errorf("Error waiting for volume: %s", err)
		state.Put("error", err)
		ui.Error(err.Error())
		return multistep.ActionHalt
	}

	state.Put("volume_id", s.volumeId)
	return multistep.ActionContinue
}

func (s *StepCreateVolume) Cleanup(state multistep.StateBag) {
	if s.volumeId == "" {
		return
	}

	oapiconn := state.Get("oapi").(*oapi.Client)
	ui := state.Get("ui").(packer.Ui)

	ui.Say("Deleting the created BSU volume...")
	_, err := oapiconn.POST_DeleteVolume(oapi.DeleteVolumeRequest{VolumeId: s.volumeId})
	if err != nil {
		ui.Error(fmt.Sprintf("Error deleting BSU volume: %s", err))
	}
}

func (s *StepCreateVolume) buildCreateVolumeInput(suregionName string, rootDevice *oapi.BlockDeviceMappingImage) (*oapi.CreateVolumeRequest, error) {
	if rootDevice == nil {
		return nil, fmt.Errorf("Couldn't find root device!")
	}

	//FIX: Temporary fix
	gibSize := rootDevice.Bsu.VolumeSize / (1024 * 1024 * 1024)
	createVolumeInput := &oapi.CreateVolumeRequest{
		SubregionName: suregionName,
		Size:          gibSize,
		SnapshotId:    rootDevice.Bsu.SnapshotId,
		VolumeType:    rootDevice.Bsu.VolumeType,
		Iops:          rootDevice.Bsu.Iops,
	}
	if s.RootVolumeSize > rootDevice.Bsu.VolumeSize {
		createVolumeInput.Size = s.RootVolumeSize
	}

	if s.RootVolumeType == "" || s.RootVolumeType == rootDevice.Bsu.VolumeType {
		return createVolumeInput, nil
	}

	if s.RootVolumeType == "io1" {
		return nil, fmt.Errorf("Root volume type cannot be io1, because existing root volume type was %s", rootDevice.Bsu.VolumeType)
	}

	createVolumeInput.VolumeType = s.RootVolumeType
	// non io1 cannot set iops
	createVolumeInput.Iops = 0

	return createVolumeInput, nil
}
<|endoftext|>